From ee26e010fcb074f073eaa6bdeeb563c42b382825 Mon Sep 17 00:00:00 2001 From: Andriy Redko Date: Thu, 18 Aug 2022 15:51:26 -0400 Subject: [PATCH 001/187] Fixing NodeGatewayStartedShards bwc (de)serialization issues (#4258) Signed-off-by: Andriy Redko Signed-off-by: Andriy Redko --- modules/repository-s3/licenses/commons-logging-1.2.jar.sha1 | 1 - .../gateway/TransportNodesListGatewayStartedShards.java | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) delete mode 100644 modules/repository-s3/licenses/commons-logging-1.2.jar.sha1 diff --git a/modules/repository-s3/licenses/commons-logging-1.2.jar.sha1 b/modules/repository-s3/licenses/commons-logging-1.2.jar.sha1 deleted file mode 100644 index f40f0242448e8..0000000000000 --- a/modules/repository-s3/licenses/commons-logging-1.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -4bfc12adfe4842bf07b657f0369c4cb522955686 \ No newline at end of file diff --git a/server/src/main/java/org/opensearch/gateway/TransportNodesListGatewayStartedShards.java b/server/src/main/java/org/opensearch/gateway/TransportNodesListGatewayStartedShards.java index 953b4def9d653..c43f539243d7a 100644 --- a/server/src/main/java/org/opensearch/gateway/TransportNodesListGatewayStartedShards.java +++ b/server/src/main/java/org/opensearch/gateway/TransportNodesListGatewayStartedShards.java @@ -373,7 +373,7 @@ public NodeGatewayStartedShards(StreamInput in) throws IOException { } else { storeException = null; } - if (in.getVersion().onOrAfter(Version.V_3_0_0) && in.readBoolean()) { + if (in.getVersion().onOrAfter(Version.V_2_3_0) && in.readBoolean()) { replicationCheckpoint = new ReplicationCheckpoint(in); } else { replicationCheckpoint = null; @@ -430,7 +430,7 @@ public void writeTo(StreamOutput out) throws IOException { } else { out.writeBoolean(false); } - if (out.getVersion().onOrAfter(Version.V_3_0_0)) { + if (out.getVersion().onOrAfter(Version.V_2_3_0)) { if (replicationCheckpoint != null) { out.writeBoolean(true); replicationCheckpoint.writeTo(out); From 4643620eda3818ef63c474f90dde6814128101c8 Mon Sep 17 00:00:00 2001 From: Daniel Widdis Date: Sun, 21 Aug 2022 10:52:20 -0700 Subject: [PATCH 002/187] Fix AbstractStringFieldDataTestCase tests to account for TotalHits lower bound (#4270) Fixes tests to account for TotalHits uncertainty as of Lucene 9. Signed-off-by: Daniel Widdis --- .../index/fielddata/AbstractStringFieldDataTestCase.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/server/src/test/java/org/opensearch/index/fielddata/AbstractStringFieldDataTestCase.java b/server/src/test/java/org/opensearch/index/fielddata/AbstractStringFieldDataTestCase.java index 763ee59a385a2..76496491b3ed4 100644 --- a/server/src/test/java/org/opensearch/index/fielddata/AbstractStringFieldDataTestCase.java +++ b/server/src/test/java/org/opensearch/index/fielddata/AbstractStringFieldDataTestCase.java @@ -52,6 +52,7 @@ import org.apache.lucene.search.SortField; import org.apache.lucene.search.TermQuery; import org.apache.lucene.search.TopFieldDocs; +import org.apache.lucene.search.TotalHits; import org.apache.lucene.search.join.QueryBitSetProducer; import org.apache.lucene.search.join.ScoreMode; import org.apache.lucene.search.join.ToParentBlockJoinQuery; @@ -340,7 +341,13 @@ public void testSortMissing(boolean first, boolean reverse) throws IOException { randomBoolean() ? numDocs : randomIntBetween(10, numDocs), new Sort(sortField) ); - assertEquals(numDocs, topDocs.totalHits.value); + // As of Lucene 9.0.0, totalHits may be a lower bound + if (topDocs.totalHits.relation == TotalHits.Relation.EQUAL_TO) { + assertEquals(numDocs, topDocs.totalHits.value); + } else { + assertTrue(1000 <= topDocs.totalHits.value); + assertTrue(numDocs >= topDocs.totalHits.value); + } BytesRef previousValue = first ? null : reverse ? UnicodeUtil.BIG_TERM : new BytesRef(); for (int i = 0; i < topDocs.scoreDocs.length; ++i) { final String docValue = searcher.doc(topDocs.scoreDocs[i].doc).get("value"); From 6629b09aad2629fafb7aad998153867c2f3fe472 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Aug 2022 09:40:48 -0400 Subject: [PATCH 003/187] Bump com.gradle.enterprise from 3.10.3 to 3.11.1 (#4273) Bumps com.gradle.enterprise from 3.10.3 to 3.11.1. --- updated-dependencies: - dependency-name: com.gradle.enterprise dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index 65dc6a13100e2..4c389b5490e7c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -10,7 +10,7 @@ */ plugins { - id "com.gradle.enterprise" version "3.10.3" + id "com.gradle.enterprise" version "3.11.1" } buildCache { From 54330560a7064434e088dad0eeb61ad0df2cfa6b Mon Sep 17 00:00:00 2001 From: Navneet Verma Date: Mon, 22 Aug 2022 13:03:22 -0700 Subject: [PATCH 004/187] Refactored the src and test of GeoHashGrid and GeoTileGrid Aggregations on GeoPoint from server folder to geo module.(#4071) (#4072) (#4180) The changes also includes: * Updated Search plugin to provide the interface so that plugins can also register the composite aggregations * Added YAML test for the geo_grid, geo_tile and composite aggregation Signed-off-by: Navneet Verma --- .../client/RestHighLevelClient.java | 6 - modules/geo/build.gradle | 9 +- .../aggregations/bucket/GeoHashGridIT.java | 39 +- .../aggregations/bucket/ShardReduceIT.java | 107 +++++ ...actGeoAggregatorModulePluginTestCase.java} | 2 +- ...BoundsIT.java => GeoBoundsITTestCase.java} | 2 +- .../metrics/GeoCentroidITTestCase.java | 84 ++++ .../org/opensearch/geo/GeoModulePlugin.java | 42 +- .../GeoTileGridValuesSourceBuilder.java | 29 +- .../bucket/composite/GeoTileValuesSource.java | 8 +- .../bucket/geogrid/BoundedCellValues.java | 2 +- .../bucket/geogrid/BucketPriorityQueue.java | 2 +- .../bucket/geogrid/CellIdSource.java | 2 +- .../bucket/geogrid/CellValues.java | 2 +- .../aggregations/bucket/geogrid/GeoGrid.java | 2 +- .../geogrid/GeoGridAggregationBuilder.java | 2 +- .../bucket/geogrid/GeoGridAggregator.java | 2 +- .../GeoHashGridAggregationBuilder.java | 4 +- .../bucket/geogrid/GeoHashGridAggregator.java | 2 +- .../geogrid/GeoHashGridAggregatorFactory.java | 2 +- .../GeoTileGridAggregationBuilder.java | 5 +- .../bucket/geogrid/GeoTileGridAggregator.java | 2 +- .../geogrid/GeoTileGridAggregatorFactory.java | 3 +- .../bucket/geogrid/InternalGeoGrid.java | 2 +- .../bucket/geogrid/InternalGeoGridBucket.java | 2 +- .../bucket/geogrid/InternalGeoHashGrid.java | 2 +- .../geogrid/InternalGeoHashGridBucket.java | 2 +- .../bucket/geogrid/InternalGeoTileGrid.java | 2 +- .../geogrid/InternalGeoTileGridBucket.java | 3 +- .../bucket/geogrid/ParsedGeoGrid.java | 2 +- .../bucket/geogrid/ParsedGeoGridBucket.java | 2 +- .../bucket/geogrid/ParsedGeoHashGrid.java | 2 +- .../geogrid/ParsedGeoHashGridBucket.java | 2 +- .../bucket/geogrid/ParsedGeoTileGrid.java | 2 +- .../geogrid/ParsedGeoTileGridBucket.java | 3 +- .../bucket/geogrid/UnboundedCellValues.java | 2 +- .../bucket/geogrid/package-info.java | 2 +- .../metrics/GeoGridAggregatorSupplier.java | 4 +- .../GeoHashGridAggregationBuilderTests.java | 21 +- .../GeoTileGridAggregationBuilderTests.java | 23 +- ...idAggregationCompositeAggregatorTests.java | 174 ++++++++ ...eGridCompositeAggregationBuilderTests.java | 50 +++ .../GeoTileGridValuesSourceBuilderTests.java | 3 +- .../geogrid/GeoGridAggregatorTestCase.java | 20 +- .../bucket/geogrid/GeoGridTestCase.java | 39 +- .../geogrid/GeoHashGridAggregatorTests.java | 2 +- .../geogrid/GeoHashGridParserTests.java | 2 +- .../bucket/geogrid/GeoHashGridTests.java | 2 +- .../geogrid/GeoTileGridAggregatorTests.java | 4 +- .../geogrid/GeoTileGridParserTests.java | 3 +- .../bucket/geogrid/GeoTileGridTests.java | 3 +- .../geo/tests/common/AggregationBuilders.java | 18 + .../common/AggregationInspectionHelper.java | 5 + .../geo/tests/common/RandomGeoGenerator.java | 11 + .../test/geo_shape/230_composite.yml | 168 ++++++++ .../test/geo_shape}/280_geohash_grid.yml | 0 .../test/geo_shape}/290_geotile_grid.yml | 0 .../test/search.aggregation/230_composite.yml | 86 ---- .../aggregations/bucket/ShardReduceIT.java | 35 -- .../aggregations/metrics/GeoCentroidIT.java | 33 -- .../org/opensearch/plugins/SearchPlugin.java | 82 ++++ .../org/opensearch/search/DocValueFormat.java | 2 +- .../org/opensearch/search/SearchModule.java | 22 +- .../aggregations/AggregationBuilders.java | 18 - .../bucket/{geogrid => }/GeoTileUtils.java | 8 +- .../CompositeAggregationBuilder.java | 50 ++- .../CompositeAggregationParsingFunction.java | 22 + .../CompositeValuesSourceBuilder.java | 6 +- .../CompositeValuesSourceConfig.java | 18 +- .../CompositeValuesSourceParserHelper.java | 78 +++- .../bucket/composite/LongValuesSource.java | 6 +- .../SingleDimensionValuesSource.java | 2 +- .../support/AggregationInspectionHelper.java | 5 - .../aggregations/support/package-info.java | 2 +- .../search/DocValueFormatTests.java | 2 +- .../aggregations/AggregationsTests.java | 4 - .../CompositeAggregationBuilderTests.java | 20 +- .../composite/CompositeAggregatorTests.java | 384 +----------------- .../bucket/geogrid/GeoTileUtilsTests.java | 13 +- .../BaseCompositeAggregatorTestCase.java | 310 ++++++++++++++ .../test/InternalAggregationTestCase.java | 6 - 81 files changed, 1395 insertions(+), 761 deletions(-) rename {server/src/internalClusterTest/java/org/opensearch => modules/geo/src/internalClusterTest/java/org/opensearch/geo}/search/aggregations/bucket/GeoHashGridIT.java (89%) create mode 100644 modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/bucket/ShardReduceIT.java rename modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/metrics/{AbstractGeoAggregatorTestCaseModulePlugin.java => AbstractGeoAggregatorModulePluginTestCase.java} (99%) rename modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/metrics/{GeoBoundsIT.java => GeoBoundsITTestCase.java} (99%) create mode 100644 modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/metrics/GeoCentroidITTestCase.java rename {server/src/main/java/org/opensearch => modules/geo/src/main/java/org/opensearch/geo}/search/aggregations/bucket/composite/GeoTileGridValuesSourceBuilder.java (87%) rename {server/src/main/java/org/opensearch => modules/geo/src/main/java/org/opensearch/geo}/search/aggregations/bucket/composite/GeoTileValuesSource.java (88%) rename {server/src/main/java/org/opensearch => modules/geo/src/main/java/org/opensearch/geo}/search/aggregations/bucket/geogrid/BoundedCellValues.java (97%) rename {server/src/main/java/org/opensearch => modules/geo/src/main/java/org/opensearch/geo}/search/aggregations/bucket/geogrid/BucketPriorityQueue.java (96%) rename {server/src/main/java/org/opensearch => modules/geo/src/main/java/org/opensearch/geo}/search/aggregations/bucket/geogrid/CellIdSource.java (98%) rename {server/src/main/java/org/opensearch => modules/geo/src/main/java/org/opensearch/geo}/search/aggregations/bucket/geogrid/CellValues.java (97%) rename {server/src/main/java/org/opensearch => modules/geo/src/main/java/org/opensearch/geo}/search/aggregations/bucket/geogrid/GeoGrid.java (96%) rename {server/src/main/java/org/opensearch => modules/geo/src/main/java/org/opensearch/geo}/search/aggregations/bucket/geogrid/GeoGridAggregationBuilder.java (99%) rename {server/src/main/java/org/opensearch => modules/geo/src/main/java/org/opensearch/geo}/search/aggregations/bucket/geogrid/GeoGridAggregator.java (99%) rename {server/src/main/java/org/opensearch => modules/geo/src/main/java/org/opensearch/geo}/search/aggregations/bucket/geogrid/GeoHashGridAggregationBuilder.java (96%) rename {server/src/main/java/org/opensearch => modules/geo/src/main/java/org/opensearch/geo}/search/aggregations/bucket/geogrid/GeoHashGridAggregator.java (97%) rename {server/src/main/java/org/opensearch => modules/geo/src/main/java/org/opensearch/geo}/search/aggregations/bucket/geogrid/GeoHashGridAggregatorFactory.java (98%) rename {server/src/main/java/org/opensearch => modules/geo/src/main/java/org/opensearch/geo}/search/aggregations/bucket/geogrid/GeoTileGridAggregationBuilder.java (95%) rename {server/src/main/java/org/opensearch => modules/geo/src/main/java/org/opensearch/geo}/search/aggregations/bucket/geogrid/GeoTileGridAggregator.java (97%) rename {server/src/main/java/org/opensearch => modules/geo/src/main/java/org/opensearch/geo}/search/aggregations/bucket/geogrid/GeoTileGridAggregatorFactory.java (97%) rename {server/src/main/java/org/opensearch => modules/geo/src/main/java/org/opensearch/geo}/search/aggregations/bucket/geogrid/InternalGeoGrid.java (99%) rename {server/src/main/java/org/opensearch => modules/geo/src/main/java/org/opensearch/geo}/search/aggregations/bucket/geogrid/InternalGeoGridBucket.java (98%) rename {server/src/main/java/org/opensearch => modules/geo/src/main/java/org/opensearch/geo}/search/aggregations/bucket/geogrid/InternalGeoHashGrid.java (97%) rename {server/src/main/java/org/opensearch => modules/geo/src/main/java/org/opensearch/geo}/search/aggregations/bucket/geogrid/InternalGeoHashGridBucket.java (96%) rename {server/src/main/java/org/opensearch => modules/geo/src/main/java/org/opensearch/geo}/search/aggregations/bucket/geogrid/InternalGeoTileGrid.java (97%) rename {server/src/main/java/org/opensearch => modules/geo/src/main/java/org/opensearch/geo}/search/aggregations/bucket/geogrid/InternalGeoTileGridBucket.java (94%) rename {server/src/main/java/org/opensearch => modules/geo/src/main/java/org/opensearch/geo}/search/aggregations/bucket/geogrid/ParsedGeoGrid.java (97%) rename {server/src/main/java/org/opensearch => modules/geo/src/main/java/org/opensearch/geo}/search/aggregations/bucket/geogrid/ParsedGeoGridBucket.java (96%) rename {server/src/main/java/org/opensearch => modules/geo/src/main/java/org/opensearch/geo}/search/aggregations/bucket/geogrid/ParsedGeoHashGrid.java (96%) rename {server/src/main/java/org/opensearch => modules/geo/src/main/java/org/opensearch/geo}/search/aggregations/bucket/geogrid/ParsedGeoHashGridBucket.java (96%) rename {server/src/main/java/org/opensearch => modules/geo/src/main/java/org/opensearch/geo}/search/aggregations/bucket/geogrid/ParsedGeoTileGrid.java (96%) rename {server/src/main/java/org/opensearch => modules/geo/src/main/java/org/opensearch/geo}/search/aggregations/bucket/geogrid/ParsedGeoTileGridBucket.java (93%) rename {server/src/main/java/org/opensearch => modules/geo/src/main/java/org/opensearch/geo}/search/aggregations/bucket/geogrid/UnboundedCellValues.java (96%) rename {server/src/main/java/org/opensearch => modules/geo/src/main/java/org/opensearch/geo}/search/aggregations/bucket/geogrid/package-info.java (79%) rename {server/src/main/java/org/opensearch => modules/geo/src/main/java/org/opensearch/geo}/search/aggregations/metrics/GeoGridAggregatorSupplier.java (93%) rename server/src/test/java/org/opensearch/search/aggregations/bucket/GeoHashGridTests.java => modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/GeoHashGridAggregationBuilderTests.java (72%) rename server/src/test/java/org/opensearch/search/aggregations/bucket/GeoTileGridTests.java => modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/GeoTileGridAggregationBuilderTests.java (70%) create mode 100644 modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/composite/GeoTileGridAggregationCompositeAggregatorTests.java create mode 100644 modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/composite/GeoTileGridCompositeAggregationBuilderTests.java rename {server/src/test/java/org/opensearch => modules/geo/src/test/java/org/opensearch/geo}/search/aggregations/bucket/composite/GeoTileGridValuesSourceBuilderTests.java (90%) rename {server/src/test/java/org/opensearch => modules/geo/src/test/java/org/opensearch/geo}/search/aggregations/bucket/geogrid/GeoGridAggregatorTestCase.java (95%) rename {server/src/test/java/org/opensearch => modules/geo/src/test/java/org/opensearch/geo}/search/aggregations/bucket/geogrid/GeoGridTestCase.java (79%) rename {server/src/test/java/org/opensearch => modules/geo/src/test/java/org/opensearch/geo}/search/aggregations/bucket/geogrid/GeoHashGridAggregatorTests.java (96%) rename {server/src/test/java/org/opensearch => modules/geo/src/test/java/org/opensearch/geo}/search/aggregations/bucket/geogrid/GeoHashGridParserTests.java (99%) rename {server/src/test/java/org/opensearch => modules/geo/src/test/java/org/opensearch/geo}/search/aggregations/bucket/geogrid/GeoHashGridTests.java (97%) rename {server/src/test/java/org/opensearch => modules/geo/src/test/java/org/opensearch/geo}/search/aggregations/bucket/geogrid/GeoTileGridAggregatorTests.java (94%) rename {server/src/test/java/org/opensearch => modules/geo/src/test/java/org/opensearch/geo}/search/aggregations/bucket/geogrid/GeoTileGridParserTests.java (97%) rename {server/src/test/java/org/opensearch => modules/geo/src/test/java/org/opensearch/geo}/search/aggregations/bucket/geogrid/GeoTileGridTests.java (94%) create mode 100644 modules/geo/src/yamlRestTest/resources/rest-api-spec/test/geo_shape/230_composite.yml rename {rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation => modules/geo/src/yamlRestTest/resources/rest-api-spec/test/geo_shape}/280_geohash_grid.yml (100%) rename {rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation => modules/geo/src/yamlRestTest/resources/rest-api-spec/test/geo_shape}/290_geotile_grid.yml (100%) rename server/src/main/java/org/opensearch/search/aggregations/bucket/{geogrid => }/GeoTileUtils.java (97%) create mode 100644 server/src/main/java/org/opensearch/search/aggregations/bucket/composite/CompositeAggregationParsingFunction.java create mode 100644 test/framework/src/main/java/org/opensearch/search/aggregations/composite/BaseCompositeAggregatorTestCase.java diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/RestHighLevelClient.java b/client/rest-high-level/src/main/java/org/opensearch/client/RestHighLevelClient.java index 7ae8f8826c5a4..28a441bdf7f7f 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/RestHighLevelClient.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/RestHighLevelClient.java @@ -108,10 +108,6 @@ import org.opensearch.search.aggregations.bucket.filter.FiltersAggregationBuilder; import org.opensearch.search.aggregations.bucket.filter.ParsedFilter; import org.opensearch.search.aggregations.bucket.filter.ParsedFilters; -import org.opensearch.search.aggregations.bucket.geogrid.GeoHashGridAggregationBuilder; -import org.opensearch.search.aggregations.bucket.geogrid.GeoTileGridAggregationBuilder; -import org.opensearch.search.aggregations.bucket.geogrid.ParsedGeoHashGrid; -import org.opensearch.search.aggregations.bucket.geogrid.ParsedGeoTileGrid; import org.opensearch.search.aggregations.bucket.global.GlobalAggregationBuilder; import org.opensearch.search.aggregations.bucket.global.ParsedGlobal; import org.opensearch.search.aggregations.bucket.histogram.AutoDateHistogramAggregationBuilder; @@ -2130,8 +2126,6 @@ static List getDefaultNamedXContents() { map.put(GlobalAggregationBuilder.NAME, (p, c) -> ParsedGlobal.fromXContent(p, (String) c)); map.put(FilterAggregationBuilder.NAME, (p, c) -> ParsedFilter.fromXContent(p, (String) c)); map.put(InternalSampler.PARSER_NAME, (p, c) -> ParsedSampler.fromXContent(p, (String) c)); - map.put(GeoHashGridAggregationBuilder.NAME, (p, c) -> ParsedGeoHashGrid.fromXContent(p, (String) c)); - map.put(GeoTileGridAggregationBuilder.NAME, (p, c) -> ParsedGeoTileGrid.fromXContent(p, (String) c)); map.put(RangeAggregationBuilder.NAME, (p, c) -> ParsedRange.fromXContent(p, (String) c)); map.put(DateRangeAggregationBuilder.NAME, (p, c) -> ParsedDateRange.fromXContent(p, (String) c)); map.put(GeoDistanceAggregationBuilder.NAME, (p, c) -> ParsedGeoDistance.fromXContent(p, (String) c)); diff --git a/modules/geo/build.gradle b/modules/geo/build.gradle index 0b8e623c24ac6..7f687a414e566 100644 --- a/modules/geo/build.gradle +++ b/modules/geo/build.gradle @@ -37,9 +37,16 @@ opensearchplugin { restResources { restApi { - includeCore '_common', 'indices', 'index', 'search' + includeCore '_common', 'indices', 'index', 'search', 'bulk' } } artifacts { restTests(project.file('src/yamlRestTest/resources/rest-api-spec/test')) } +/** + * These compiler arguments needs to be removed, as there are raw types being used in the GeoGrid and GeoTile aggregations. + */ +tasks.withType(JavaCompile).configureEach { + options.compilerArgs -= '-Xlint:rawtypes' + options.compilerArgs -= '-Xlint:unchecked' +} diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/GeoHashGridIT.java b/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/bucket/GeoHashGridIT.java similarity index 89% rename from server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/GeoHashGridIT.java rename to modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/bucket/GeoHashGridIT.java index 56d918feef9d8..6ab7dd5254679 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/GeoHashGridIT.java +++ b/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/bucket/GeoHashGridIT.java @@ -29,7 +29,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket; +package org.opensearch.geo.search.aggregations.bucket; import com.carrotsearch.hppc.ObjectIntHashMap; import com.carrotsearch.hppc.ObjectIntMap; @@ -41,12 +41,12 @@ import org.opensearch.common.geo.GeoPoint; import org.opensearch.common.settings.Settings; import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.geo.GeoModulePluginIntegTestCase; +import org.opensearch.geo.search.aggregations.bucket.geogrid.GeoGrid; +import org.opensearch.geo.tests.common.AggregationBuilders; import org.opensearch.index.query.GeoBoundingBoxQueryBuilder; -import org.opensearch.search.aggregations.AggregationBuilders; import org.opensearch.search.aggregations.InternalAggregation; import org.opensearch.search.aggregations.bucket.filter.Filter; -import org.opensearch.search.aggregations.bucket.geogrid.GeoGrid; -import org.opensearch.search.aggregations.bucket.geogrid.GeoGrid.Bucket; import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.VersionUtils; @@ -57,17 +57,16 @@ import java.util.Random; import java.util.Set; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.geometry.utils.Geohash.PRECISION; import static org.opensearch.geometry.utils.Geohash.stringEncode; -import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; -import static org.opensearch.search.aggregations.AggregationBuilders.geohashGrid; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertSearchResponse; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.equalTo; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class GeoHashGridIT extends OpenSearchIntegTestCase { +public class GeoHashGridIT extends GeoModulePluginIntegTestCase { @Override protected boolean forbidPrivateIndexSettings() { @@ -158,13 +157,13 @@ public void setupSuiteScopeCluster() throws Exception { public void testSimple() throws Exception { for (int precision = 1; precision <= PRECISION; precision++) { SearchResponse response = client().prepareSearch("idx") - .addAggregation(geohashGrid("geohashgrid").field("location").precision(precision)) + .addAggregation(AggregationBuilders.geohashGrid("geohashgrid").field("location").precision(precision)) .get(); assertSearchResponse(response); GeoGrid geoGrid = response.getAggregations().get("geohashgrid"); - List buckets = geoGrid.getBuckets(); + List buckets = geoGrid.getBuckets(); Object[] propertiesKeys = (Object[]) ((InternalAggregation) geoGrid).getProperty("_key"); Object[] propertiesDocCounts = (Object[]) ((InternalAggregation) geoGrid).getProperty("_count"); for (int i = 0; i < buckets.size(); i++) { @@ -185,7 +184,7 @@ public void testSimple() throws Exception { public void testMultivalued() throws Exception { for (int precision = 1; precision <= PRECISION; precision++) { SearchResponse response = client().prepareSearch("multi_valued_idx") - .addAggregation(geohashGrid("geohashgrid").field("location").precision(precision)) + .addAggregation(AggregationBuilders.geohashGrid("geohashgrid").field("location").precision(precision)) .get(); assertSearchResponse(response); @@ -208,8 +207,8 @@ public void testFiltered() throws Exception { for (int precision = 1; precision <= PRECISION; precision++) { SearchResponse response = client().prepareSearch("idx") .addAggregation( - AggregationBuilders.filter("filtered", bbox) - .subAggregation(geohashGrid("geohashgrid").field("location").precision(precision)) + org.opensearch.search.aggregations.AggregationBuilders.filter("filtered", bbox) + .subAggregation(AggregationBuilders.geohashGrid("geohashgrid").field("location").precision(precision)) ) .get(); @@ -233,7 +232,7 @@ public void testFiltered() throws Exception { public void testUnmapped() throws Exception { for (int precision = 1; precision <= PRECISION; precision++) { SearchResponse response = client().prepareSearch("idx_unmapped") - .addAggregation(geohashGrid("geohashgrid").field("location").precision(precision)) + .addAggregation(AggregationBuilders.geohashGrid("geohashgrid").field("location").precision(precision)) .get(); assertSearchResponse(response); @@ -247,7 +246,7 @@ public void testUnmapped() throws Exception { public void testPartiallyUnmapped() throws Exception { for (int precision = 1; precision <= PRECISION; precision++) { SearchResponse response = client().prepareSearch("idx", "idx_unmapped") - .addAggregation(geohashGrid("geohashgrid").field("location").precision(precision)) + .addAggregation(AggregationBuilders.geohashGrid("geohashgrid").field("location").precision(precision)) .get(); assertSearchResponse(response); @@ -267,7 +266,9 @@ public void testPartiallyUnmapped() throws Exception { public void testTopMatch() throws Exception { for (int precision = 1; precision <= PRECISION; precision++) { SearchResponse response = client().prepareSearch("idx") - .addAggregation(geohashGrid("geohashgrid").field("location").size(1).shardSize(100).precision(precision)) + .addAggregation( + AggregationBuilders.geohashGrid("geohashgrid").field("location").size(1).shardSize(100).precision(precision) + ) .get(); assertSearchResponse(response); @@ -296,7 +297,7 @@ public void testSizeIsZero() { IllegalArgumentException exception = expectThrows( IllegalArgumentException.class, () -> client().prepareSearch("idx") - .addAggregation(geohashGrid("geohashgrid").field("location").size(size).shardSize(shardSize)) + .addAggregation(AggregationBuilders.geohashGrid("geohashgrid").field("location").size(size).shardSize(shardSize)) .get() ); assertThat(exception.getMessage(), containsString("[size] must be greater than 0. Found [0] in [geohashgrid]")); @@ -308,7 +309,7 @@ public void testShardSizeIsZero() { IllegalArgumentException exception = expectThrows( IllegalArgumentException.class, () -> client().prepareSearch("idx") - .addAggregation(geohashGrid("geohashgrid").field("location").size(size).shardSize(shardSize)) + .addAggregation(AggregationBuilders.geohashGrid("geohashgrid").field("location").size(size).shardSize(shardSize)) .get() ); assertThat(exception.getMessage(), containsString("[shardSize] must be greater than 0. Found [0] in [geohashgrid]")); diff --git a/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/bucket/ShardReduceIT.java b/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/bucket/ShardReduceIT.java new file mode 100644 index 0000000000000..5b4dd052a2f65 --- /dev/null +++ b/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/bucket/ShardReduceIT.java @@ -0,0 +1,107 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.geo.search.aggregations.bucket; + +import org.opensearch.action.index.IndexRequestBuilder; +import org.opensearch.action.search.SearchResponse; +import org.opensearch.geo.GeoModulePluginIntegTestCase; +import org.opensearch.geo.search.aggregations.bucket.geogrid.GeoGrid; +import org.opensearch.geo.tests.common.AggregationBuilders; +import org.opensearch.geometry.utils.Geohash; +import org.opensearch.index.query.QueryBuilders; +import org.opensearch.search.aggregations.bucket.histogram.DateHistogramInterval; +import org.opensearch.search.aggregations.bucket.histogram.Histogram; +import org.opensearch.test.OpenSearchIntegTestCase; + +import static org.hamcrest.Matchers.equalTo; +import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.search.aggregations.AggregationBuilders.dateHistogram; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertSearchResponse; + +/** + * Tests making sure that the reduce is propagated to all aggregations in the hierarchy when executing on a single shard + * These tests are based on the date histogram in combination of min_doc_count=0. In order for the date histogram to + * compute empty buckets, its {@code reduce()} method must be called. So by adding the date histogram under other buckets, + * we can make sure that the reduce is properly propagated by checking that empty buckets were created. + */ +@OpenSearchIntegTestCase.SuiteScopeTestCase +public class ShardReduceIT extends GeoModulePluginIntegTestCase { + + private IndexRequestBuilder indexDoc(String date, int value) throws Exception { + return client().prepareIndex("idx") + .setSource( + jsonBuilder().startObject() + .field("value", value) + .field("ip", "10.0.0." + value) + .field("location", Geohash.stringEncode(5, 52, Geohash.PRECISION)) + .field("date", date) + .field("term-l", 1) + .field("term-d", 1.5) + .field("term-s", "term") + .startObject("nested") + .field("date", date) + .endObject() + .endObject() + ); + } + + @Override + public void setupSuiteScopeCluster() throws Exception { + assertAcked( + prepareCreate("idx").setMapping( + "nested", + "type=nested", + "ip", + "type=ip", + "location", + "type=geo_point", + "term-s", + "type=keyword" + ) + ); + + indexRandom(true, indexDoc("2014-01-01", 1), indexDoc("2014-01-02", 2), indexDoc("2014-01-04", 3)); + ensureSearchable(); + } + + public void testGeoHashGrid() throws Exception { + SearchResponse response = client().prepareSearch("idx") + .setQuery(QueryBuilders.matchAllQuery()) + .addAggregation( + AggregationBuilders.geohashGrid("grid") + .field("location") + .subAggregation(dateHistogram("histo").field("date").fixedInterval(DateHistogramInterval.DAY).minDocCount(0)) + ) + .get(); + + assertSearchResponse(response); + + GeoGrid grid = response.getAggregations().get("grid"); + Histogram histo = grid.getBuckets().iterator().next().getAggregations().get("histo"); + assertThat(histo.getBuckets().size(), equalTo(4)); + } + + public void testGeoTileGrid() throws Exception { + SearchResponse response = client().prepareSearch("idx") + .setQuery(QueryBuilders.matchAllQuery()) + .addAggregation( + AggregationBuilders.geotileGrid("grid") + .field("location") + .subAggregation(dateHistogram("histo").field("date").fixedInterval(DateHistogramInterval.DAY).minDocCount(0)) + ) + .get(); + + assertSearchResponse(response); + + GeoGrid grid = response.getAggregations().get("grid"); + Histogram histo = grid.getBuckets().iterator().next().getAggregations().get("histo"); + assertThat(histo.getBuckets().size(), equalTo(4)); + } +} diff --git a/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/metrics/AbstractGeoAggregatorTestCaseModulePlugin.java b/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/metrics/AbstractGeoAggregatorModulePluginTestCase.java similarity index 99% rename from modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/metrics/AbstractGeoAggregatorTestCaseModulePlugin.java rename to modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/metrics/AbstractGeoAggregatorModulePluginTestCase.java index 0065cca7d6101..92987d407f51d 100644 --- a/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/metrics/AbstractGeoAggregatorTestCaseModulePlugin.java +++ b/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/metrics/AbstractGeoAggregatorModulePluginTestCase.java @@ -42,7 +42,7 @@ * to copy the code as we cannot depend on this class. * GitHub issue */ -public abstract class AbstractGeoAggregatorTestCaseModulePlugin extends GeoModulePluginIntegTestCase { +public abstract class AbstractGeoAggregatorModulePluginTestCase extends GeoModulePluginIntegTestCase { protected static final String SINGLE_VALUED_FIELD_NAME = "geo_value"; protected static final String MULTI_VALUED_FIELD_NAME = "geo_values"; diff --git a/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/metrics/GeoBoundsIT.java b/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/metrics/GeoBoundsITTestCase.java similarity index 99% rename from modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/metrics/GeoBoundsIT.java rename to modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/metrics/GeoBoundsITTestCase.java index 5cbd98a4936e4..8cc82da12d69a 100644 --- a/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/metrics/GeoBoundsIT.java +++ b/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/metrics/GeoBoundsITTestCase.java @@ -57,7 +57,7 @@ import static org.opensearch.geo.tests.common.AggregationBuilders.geoBounds; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class GeoBoundsIT extends AbstractGeoAggregatorTestCaseModulePlugin { +public class GeoBoundsITTestCase extends AbstractGeoAggregatorModulePluginTestCase { private static final String aggName = "geoBounds"; public void testSingleValuedField() throws Exception { diff --git a/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/metrics/GeoCentroidITTestCase.java b/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/metrics/GeoCentroidITTestCase.java new file mode 100644 index 0000000000000..e6d45e27b8f70 --- /dev/null +++ b/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/metrics/GeoCentroidITTestCase.java @@ -0,0 +1,84 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.geo.search.aggregations.metrics; + +import org.opensearch.action.search.SearchResponse; +import org.opensearch.common.geo.GeoPoint; +import org.opensearch.geo.search.aggregations.bucket.geogrid.GeoGrid; +import org.opensearch.geo.tests.common.AggregationBuilders; +import org.opensearch.search.aggregations.metrics.GeoCentroid; +import org.opensearch.test.OpenSearchIntegTestCase; + +import java.util.List; + +import static org.hamcrest.Matchers.closeTo; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.notNullValue; +import static org.opensearch.search.aggregations.AggregationBuilders.geoCentroid; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertSearchResponse; + +@OpenSearchIntegTestCase.SuiteScopeTestCase +public class GeoCentroidITTestCase extends AbstractGeoAggregatorModulePluginTestCase { + private static final String aggName = "geoCentroid"; + + public void testSingleValueFieldAsSubAggToGeohashGrid() throws Exception { + SearchResponse response = client().prepareSearch(HIGH_CARD_IDX_NAME) + .addAggregation( + AggregationBuilders.geohashGrid("geoGrid") + .field(SINGLE_VALUED_FIELD_NAME) + .subAggregation(geoCentroid(aggName).field(SINGLE_VALUED_FIELD_NAME)) + ) + .get(); + assertSearchResponse(response); + + GeoGrid grid = response.getAggregations().get("geoGrid"); + assertThat(grid, notNullValue()); + assertThat(grid.getName(), equalTo("geoGrid")); + List buckets = grid.getBuckets(); + for (GeoGrid.Bucket cell : buckets) { + String geohash = cell.getKeyAsString(); + GeoPoint expectedCentroid = expectedCentroidsForGeoHash.get(geohash); + GeoCentroid centroidAgg = cell.getAggregations().get(aggName); + assertThat( + "Geohash " + geohash + " has wrong centroid latitude ", + expectedCentroid.lat(), + closeTo(centroidAgg.centroid().lat(), GEOHASH_TOLERANCE) + ); + assertThat( + "Geohash " + geohash + " has wrong centroid longitude", + expectedCentroid.lon(), + closeTo(centroidAgg.centroid().lon(), GEOHASH_TOLERANCE) + ); + } + } +} diff --git a/modules/geo/src/main/java/org/opensearch/geo/GeoModulePlugin.java b/modules/geo/src/main/java/org/opensearch/geo/GeoModulePlugin.java index 64aac66b7eef3..25dcf8db2c407 100644 --- a/modules/geo/src/main/java/org/opensearch/geo/GeoModulePlugin.java +++ b/modules/geo/src/main/java/org/opensearch/geo/GeoModulePlugin.java @@ -32,6 +32,12 @@ package org.opensearch.geo; +import org.opensearch.geo.search.aggregations.bucket.composite.GeoTileGridValuesSourceBuilder; +import org.opensearch.geo.search.aggregations.bucket.geogrid.GeoHashGridAggregationBuilder; +import org.opensearch.geo.search.aggregations.bucket.geogrid.GeoTileGridAggregationBuilder; +import org.opensearch.geo.search.aggregations.bucket.geogrid.GeoTileGridAggregator; +import org.opensearch.geo.search.aggregations.bucket.geogrid.InternalGeoHashGrid; +import org.opensearch.geo.search.aggregations.bucket.geogrid.InternalGeoTileGrid; import org.opensearch.geo.search.aggregations.metrics.GeoBounds; import org.opensearch.geo.search.aggregations.metrics.GeoBoundsAggregationBuilder; import org.opensearch.geo.search.aggregations.metrics.InternalGeoBounds; @@ -40,6 +46,7 @@ import org.opensearch.plugins.MapperPlugin; import org.opensearch.plugins.Plugin; import org.opensearch.plugins.SearchPlugin; +import org.opensearch.search.aggregations.bucket.composite.CompositeAggregation; import java.util.Collections; import java.util.List; @@ -57,11 +64,42 @@ public Map getMappers() { */ @Override public List getAggregations() { - final AggregationSpec spec = new AggregationSpec( + final AggregationSpec geoBounds = new AggregationSpec( GeoBoundsAggregationBuilder.NAME, GeoBoundsAggregationBuilder::new, GeoBoundsAggregationBuilder.PARSER ).addResultReader(InternalGeoBounds::new).setAggregatorRegistrar(GeoBoundsAggregationBuilder::registerAggregators); - return Collections.singletonList(spec); + + final AggregationSpec geoHashGrid = new AggregationSpec( + GeoHashGridAggregationBuilder.NAME, + GeoHashGridAggregationBuilder::new, + GeoHashGridAggregationBuilder.PARSER + ).addResultReader(InternalGeoHashGrid::new).setAggregatorRegistrar(GeoHashGridAggregationBuilder::registerAggregators); + + final AggregationSpec geoTileGrid = new AggregationSpec( + GeoTileGridAggregationBuilder.NAME, + GeoTileGridAggregationBuilder::new, + GeoTileGridAggregationBuilder.PARSER + ).addResultReader(InternalGeoTileGrid::new).setAggregatorRegistrar(GeoTileGridAggregationBuilder::registerAggregators); + return List.of(geoBounds, geoHashGrid, geoTileGrid); + } + + /** + * Registering the {@link GeoTileGridAggregator} in the {@link CompositeAggregation}. + * + * @return a {@link List} of {@link CompositeAggregationSpec} + */ + @Override + public List getCompositeAggregations() { + return Collections.singletonList( + new CompositeAggregationSpec( + GeoTileGridValuesSourceBuilder::register, + GeoTileGridValuesSourceBuilder.class, + GeoTileGridValuesSourceBuilder.COMPOSITE_AGGREGATION_SERIALISATION_BYTE_CODE, + GeoTileGridValuesSourceBuilder::new, + GeoTileGridValuesSourceBuilder::parse, + GeoTileGridValuesSourceBuilder.TYPE + ) + ); } } diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/composite/GeoTileGridValuesSourceBuilder.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/composite/GeoTileGridValuesSourceBuilder.java similarity index 87% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/composite/GeoTileGridValuesSourceBuilder.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/composite/GeoTileGridValuesSourceBuilder.java index 4b01a08d29a43..84d5943da287f 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/composite/GeoTileGridValuesSourceBuilder.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/composite/GeoTileGridValuesSourceBuilder.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.composite; +package org.opensearch.geo.search.aggregations.bucket.composite; import org.apache.lucene.index.IndexReader; import org.opensearch.LegacyESVersion; @@ -43,12 +43,15 @@ import org.opensearch.common.xcontent.ObjectParser; import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.geo.search.aggregations.bucket.geogrid.CellIdSource; +import org.opensearch.geo.search.aggregations.bucket.geogrid.GeoTileGridAggregationBuilder; import org.opensearch.index.mapper.MappedFieldType; import org.opensearch.index.query.QueryShardContext; import org.opensearch.search.DocValueFormat; -import org.opensearch.search.aggregations.bucket.geogrid.CellIdSource; -import org.opensearch.search.aggregations.bucket.geogrid.GeoTileGridAggregationBuilder; -import org.opensearch.search.aggregations.bucket.geogrid.GeoTileUtils; +import org.opensearch.search.aggregations.bucket.composite.CompositeValuesSourceBuilder; +import org.opensearch.search.aggregations.bucket.composite.CompositeValuesSourceConfig; +import org.opensearch.search.aggregations.bucket.composite.CompositeValuesSourceParserHelper; +import org.opensearch.search.aggregations.bucket.GeoTileUtils; import org.opensearch.search.aggregations.bucket.missing.MissingOrder; import org.opensearch.search.aggregations.support.CoreValuesSourceType; import org.opensearch.search.aggregations.support.ValuesSource; @@ -88,13 +91,19 @@ CompositeValuesSourceConfig apply( ); } - static final String TYPE = "geotile_grid"; + public static final String TYPE = "geotile_grid"; + /* + use the TYPE parameter instead of Byte code. The byte code is added for backward compatibility and will be + removed in the next version. + */ + @Deprecated + public static final Byte COMPOSITE_AGGREGATION_SERIALISATION_BYTE_CODE = 3; static final ValuesSourceRegistry.RegistryKey REGISTRY_KEY = new ValuesSourceRegistry.RegistryKey( TYPE, GeoTileCompositeSuppier.class ); - private static final ObjectParser PARSER; + static final ObjectParser PARSER; static { PARSER = new ObjectParser<>(GeoTileGridValuesSourceBuilder.TYPE); PARSER.declareInt(GeoTileGridValuesSourceBuilder::precision, new ParseField("precision")); @@ -106,11 +115,11 @@ CompositeValuesSourceConfig apply( CompositeValuesSourceParserHelper.declareValuesSourceFields(PARSER); } - static GeoTileGridValuesSourceBuilder parse(String name, XContentParser parser) throws IOException { + public static GeoTileGridValuesSourceBuilder parse(String name, XContentParser parser) throws IOException { return PARSER.parse(parser, new GeoTileGridValuesSourceBuilder(name), null); } - static void register(ValuesSourceRegistry.Builder builder) { + public static void register(ValuesSourceRegistry.Builder builder) { builder.register( REGISTRY_KEY, @@ -163,7 +172,7 @@ static void register(ValuesSourceRegistry.Builder builder) { super(name); } - GeoTileGridValuesSourceBuilder(StreamInput in) throws IOException { + public GeoTileGridValuesSourceBuilder(StreamInput in) throws IOException { super(in); this.precision = in.readInt(); if (in.getVersion().onOrAfter(LegacyESVersion.V_7_6_0)) { @@ -203,7 +212,7 @@ protected void doXContentBody(XContentBuilder builder, Params params) throws IOE } @Override - String type() { + protected String type() { return TYPE; } diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/composite/GeoTileValuesSource.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/composite/GeoTileValuesSource.java similarity index 88% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/composite/GeoTileValuesSource.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/composite/GeoTileValuesSource.java index 819dfc573bbe4..303e577e99e7b 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/composite/GeoTileValuesSource.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/composite/GeoTileValuesSource.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.composite; +package org.opensearch.geo.search.aggregations.bucket.composite; import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.SortedNumericDocValues; @@ -38,7 +38,9 @@ import org.opensearch.common.util.BigArrays; import org.opensearch.index.mapper.MappedFieldType; import org.opensearch.search.DocValueFormat; -import org.opensearch.search.aggregations.bucket.geogrid.GeoTileUtils; +import org.opensearch.search.aggregations.bucket.composite.LongValuesSource; +import org.opensearch.search.aggregations.bucket.composite.SingleDimensionValuesSource; +import org.opensearch.search.aggregations.bucket.GeoTileUtils; import org.opensearch.search.aggregations.bucket.missing.MissingOrder; import java.io.IOException; @@ -68,7 +70,7 @@ class GeoTileValuesSource extends LongValuesSource { } @Override - void setAfter(Comparable value) { + protected void setAfter(Comparable value) { if (missingBucket && value == null) { afterValue = null; } else if (value instanceof Number) { diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/BoundedCellValues.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/BoundedCellValues.java similarity index 97% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/BoundedCellValues.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/BoundedCellValues.java index ba824fc8f21dd..06d2dcaee3932 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/BoundedCellValues.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/BoundedCellValues.java @@ -29,7 +29,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.common.geo.GeoBoundingBox; import org.opensearch.index.fielddata.MultiGeoPointValues; diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/BucketPriorityQueue.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/BucketPriorityQueue.java similarity index 96% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/BucketPriorityQueue.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/BucketPriorityQueue.java index d6cfde0c46eae..70d0552b3e80b 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/BucketPriorityQueue.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/BucketPriorityQueue.java @@ -29,7 +29,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.apache.lucene.util.PriorityQueue; diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/CellIdSource.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/CellIdSource.java similarity index 98% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/CellIdSource.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/CellIdSource.java index 12d9043a2fd5f..d40029e9a762d 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/CellIdSource.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/CellIdSource.java @@ -29,7 +29,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.SortedNumericDocValues; diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/CellValues.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/CellValues.java similarity index 97% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/CellValues.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/CellValues.java index 9dc357659aae8..d01896c8136fa 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/CellValues.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/CellValues.java @@ -29,7 +29,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.index.fielddata.AbstractSortingNumericDocValues; import org.opensearch.index.fielddata.MultiGeoPointValues; diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoGrid.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoGrid.java similarity index 96% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoGrid.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoGrid.java index cfdb08f9ee3d7..4ae888640efc8 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoGrid.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoGrid.java @@ -29,7 +29,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.search.aggregations.bucket.MultiBucketsAggregation; diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoGridAggregationBuilder.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoGridAggregationBuilder.java similarity index 99% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoGridAggregationBuilder.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoGridAggregationBuilder.java index b08c40268c5cf..4a904b3aa2b16 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoGridAggregationBuilder.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoGridAggregationBuilder.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.LegacyESVersion; import org.opensearch.OpenSearchException; diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoGridAggregator.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoGridAggregator.java similarity index 99% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoGridAggregator.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoGridAggregator.java index 1ef8ba6c697f4..909772c61a960 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoGridAggregator.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoGridAggregator.java @@ -29,7 +29,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.SortedNumericDocValues; diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoHashGridAggregationBuilder.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoHashGridAggregationBuilder.java similarity index 96% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoHashGridAggregationBuilder.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoHashGridAggregationBuilder.java index 4049bf2c73640..bbaf9613fb216 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoHashGridAggregationBuilder.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoHashGridAggregationBuilder.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.common.geo.GeoBoundingBox; import org.opensearch.common.geo.GeoUtils; @@ -40,7 +40,7 @@ import org.opensearch.search.aggregations.AggregationBuilder; import org.opensearch.search.aggregations.AggregatorFactories; import org.opensearch.search.aggregations.AggregatorFactory; -import org.opensearch.search.aggregations.metrics.GeoGridAggregatorSupplier; +import org.opensearch.geo.search.aggregations.metrics.GeoGridAggregatorSupplier; import org.opensearch.search.aggregations.support.ValuesSourceAggregatorFactory; import org.opensearch.search.aggregations.support.ValuesSourceConfig; import org.opensearch.search.aggregations.support.ValuesSourceRegistry; diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoHashGridAggregator.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoHashGridAggregator.java similarity index 97% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoHashGridAggregator.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoHashGridAggregator.java index 1106320c7431f..6ca7a4d8a9cb8 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoHashGridAggregator.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoHashGridAggregator.java @@ -29,7 +29,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.search.aggregations.Aggregator; import org.opensearch.search.aggregations.AggregatorFactories; diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoHashGridAggregatorFactory.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoHashGridAggregatorFactory.java similarity index 98% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoHashGridAggregatorFactory.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoHashGridAggregatorFactory.java index cdc801aaedffb..1914c07e831f7 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoHashGridAggregatorFactory.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoHashGridAggregatorFactory.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.common.geo.GeoBoundingBox; import org.opensearch.geometry.utils.Geohash; diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileGridAggregationBuilder.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoTileGridAggregationBuilder.java similarity index 95% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileGridAggregationBuilder.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoTileGridAggregationBuilder.java index f73360e3cb826..76ad515f34fe5 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileGridAggregationBuilder.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoTileGridAggregationBuilder.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.common.geo.GeoBoundingBox; import org.opensearch.common.io.stream.StreamInput; @@ -39,7 +39,8 @@ import org.opensearch.search.aggregations.AggregationBuilder; import org.opensearch.search.aggregations.AggregatorFactories; import org.opensearch.search.aggregations.AggregatorFactory; -import org.opensearch.search.aggregations.metrics.GeoGridAggregatorSupplier; +import org.opensearch.geo.search.aggregations.metrics.GeoGridAggregatorSupplier; +import org.opensearch.search.aggregations.bucket.GeoTileUtils; import org.opensearch.search.aggregations.support.ValuesSourceAggregatorFactory; import org.opensearch.search.aggregations.support.ValuesSourceConfig; import org.opensearch.search.aggregations.support.ValuesSourceRegistry; diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileGridAggregator.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoTileGridAggregator.java similarity index 97% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileGridAggregator.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoTileGridAggregator.java index 7a2b908148c4c..a205a9afde41e 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileGridAggregator.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoTileGridAggregator.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.search.aggregations.Aggregator; import org.opensearch.search.aggregations.AggregatorFactories; diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileGridAggregatorFactory.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoTileGridAggregatorFactory.java similarity index 97% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileGridAggregatorFactory.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoTileGridAggregatorFactory.java index ef8cd11a22498..b830988a3d410 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileGridAggregatorFactory.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoTileGridAggregatorFactory.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.common.geo.GeoBoundingBox; import org.opensearch.index.query.QueryShardContext; @@ -40,6 +40,7 @@ import org.opensearch.search.aggregations.CardinalityUpperBound; import org.opensearch.search.aggregations.InternalAggregation; import org.opensearch.search.aggregations.NonCollectingAggregator; +import org.opensearch.search.aggregations.bucket.GeoTileUtils; import org.opensearch.search.aggregations.support.CoreValuesSourceType; import org.opensearch.search.aggregations.support.ValuesSource; import org.opensearch.search.aggregations.support.ValuesSourceAggregatorFactory; diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/InternalGeoGrid.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/InternalGeoGrid.java similarity index 99% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/InternalGeoGrid.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/InternalGeoGrid.java index 94a5ad5717854..9dbed7b27307a 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/InternalGeoGrid.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/InternalGeoGrid.java @@ -29,7 +29,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.io.stream.StreamOutput; diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/InternalGeoGridBucket.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/InternalGeoGridBucket.java similarity index 98% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/InternalGeoGridBucket.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/InternalGeoGridBucket.java index a187bfefb661f..93fcdbd098400 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/InternalGeoGridBucket.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/InternalGeoGridBucket.java @@ -29,7 +29,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.io.stream.StreamOutput; diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/InternalGeoHashGrid.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/InternalGeoHashGrid.java similarity index 97% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/InternalGeoHashGrid.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/InternalGeoHashGrid.java index 7811b8774d04f..ff1247300939a 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/InternalGeoHashGrid.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/InternalGeoHashGrid.java @@ -29,7 +29,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.common.io.stream.StreamInput; import org.opensearch.search.aggregations.InternalAggregations; diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/InternalGeoHashGridBucket.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/InternalGeoHashGridBucket.java similarity index 96% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/InternalGeoHashGridBucket.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/InternalGeoHashGridBucket.java index f9c45dc41ceb1..659909e868651 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/InternalGeoHashGridBucket.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/InternalGeoHashGridBucket.java @@ -29,7 +29,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.common.geo.GeoPoint; import org.opensearch.common.io.stream.StreamInput; diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/InternalGeoTileGrid.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/InternalGeoTileGrid.java similarity index 97% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/InternalGeoTileGrid.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/InternalGeoTileGrid.java index efbd9a05d6a4d..fa544b5893f0c 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/InternalGeoTileGrid.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/InternalGeoTileGrid.java @@ -29,7 +29,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.common.io.stream.StreamInput; import org.opensearch.search.aggregations.InternalAggregations; diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/InternalGeoTileGridBucket.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/InternalGeoTileGridBucket.java similarity index 94% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/InternalGeoTileGridBucket.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/InternalGeoTileGridBucket.java index f200f55232e00..65d736cfceb32 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/InternalGeoTileGridBucket.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/InternalGeoTileGridBucket.java @@ -30,11 +30,12 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.common.geo.GeoPoint; import org.opensearch.common.io.stream.StreamInput; import org.opensearch.search.aggregations.InternalAggregations; +import org.opensearch.search.aggregations.bucket.GeoTileUtils; import java.io.IOException; diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/ParsedGeoGrid.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/ParsedGeoGrid.java similarity index 97% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/ParsedGeoGrid.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/ParsedGeoGrid.java index 3f85cf350c89c..adfffeddba59d 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/ParsedGeoGrid.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/ParsedGeoGrid.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.common.CheckedFunction; import org.opensearch.common.xcontent.ObjectParser; diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/ParsedGeoGridBucket.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/ParsedGeoGridBucket.java similarity index 96% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/ParsedGeoGridBucket.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/ParsedGeoGridBucket.java index 08e5c15188ee6..80124cda50b19 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/ParsedGeoGridBucket.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/ParsedGeoGridBucket.java @@ -29,7 +29,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.search.aggregations.Aggregation; diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/ParsedGeoHashGrid.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/ParsedGeoHashGrid.java similarity index 96% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/ParsedGeoHashGrid.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/ParsedGeoHashGrid.java index f20f972c1ce0a..109524e755c4d 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/ParsedGeoHashGrid.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/ParsedGeoHashGrid.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.common.xcontent.ObjectParser; import org.opensearch.common.xcontent.XContentParser; diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/ParsedGeoHashGridBucket.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/ParsedGeoHashGridBucket.java similarity index 96% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/ParsedGeoHashGridBucket.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/ParsedGeoHashGridBucket.java index 05c7a1c8d1663..4e6e454b08324 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/ParsedGeoHashGridBucket.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/ParsedGeoHashGridBucket.java @@ -29,7 +29,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.common.geo.GeoPoint; import org.opensearch.common.xcontent.XContentParser; diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/ParsedGeoTileGrid.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/ParsedGeoTileGrid.java similarity index 96% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/ParsedGeoTileGrid.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/ParsedGeoTileGrid.java index 06915cc4210e1..8734c96a15578 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/ParsedGeoTileGrid.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/ParsedGeoTileGrid.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.common.xcontent.ObjectParser; import org.opensearch.common.xcontent.XContentParser; diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/ParsedGeoTileGridBucket.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/ParsedGeoTileGridBucket.java similarity index 93% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/ParsedGeoTileGridBucket.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/ParsedGeoTileGridBucket.java index c8dec16f322ef..fd47c35f13de1 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/ParsedGeoTileGridBucket.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/ParsedGeoTileGridBucket.java @@ -30,10 +30,11 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.common.geo.GeoPoint; import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.search.aggregations.bucket.GeoTileUtils; import java.io.IOException; diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/UnboundedCellValues.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/UnboundedCellValues.java similarity index 96% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/UnboundedCellValues.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/UnboundedCellValues.java index f5a139cdb8d9d..c628c7bfdc8ec 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/UnboundedCellValues.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/UnboundedCellValues.java @@ -29,7 +29,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.common.geo.GeoBoundingBox; import org.opensearch.index.fielddata.MultiGeoPointValues; diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/package-info.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/package-info.java similarity index 79% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/package-info.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/package-info.java index c59685e06cf79..d9183a0f742ef 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/package-info.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/package-info.java @@ -7,4 +7,4 @@ */ /** geo_grid Aggregation package. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; diff --git a/server/src/main/java/org/opensearch/search/aggregations/metrics/GeoGridAggregatorSupplier.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/metrics/GeoGridAggregatorSupplier.java similarity index 93% rename from server/src/main/java/org/opensearch/search/aggregations/metrics/GeoGridAggregatorSupplier.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/metrics/GeoGridAggregatorSupplier.java index 183c64f4e4af2..43ccb8b89545a 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/metrics/GeoGridAggregatorSupplier.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/metrics/GeoGridAggregatorSupplier.java @@ -30,13 +30,13 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.metrics; +package org.opensearch.geo.search.aggregations.metrics; import org.opensearch.common.geo.GeoBoundingBox; +import org.opensearch.geo.search.aggregations.bucket.geogrid.GeoGridAggregator; import org.opensearch.search.aggregations.Aggregator; import org.opensearch.search.aggregations.AggregatorFactories; import org.opensearch.search.aggregations.CardinalityUpperBound; -import org.opensearch.search.aggregations.bucket.geogrid.GeoGridAggregator; import org.opensearch.search.aggregations.support.ValuesSource; import org.opensearch.search.internal.SearchContext; diff --git a/server/src/test/java/org/opensearch/search/aggregations/bucket/GeoHashGridTests.java b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/GeoHashGridAggregationBuilderTests.java similarity index 72% rename from server/src/test/java/org/opensearch/search/aggregations/bucket/GeoHashGridTests.java rename to modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/GeoHashGridAggregationBuilderTests.java index 5e230a445ec98..00cb162e64c19 100644 --- a/server/src/test/java/org/opensearch/search/aggregations/bucket/GeoHashGridTests.java +++ b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/GeoHashGridAggregationBuilderTests.java @@ -30,14 +30,23 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket; +package org.opensearch.geo.search.aggregations.bucket; -import org.opensearch.common.geo.GeoBoundingBoxTests; +import org.opensearch.geo.GeoModulePlugin; +import org.opensearch.geo.search.aggregations.bucket.geogrid.GeoGridAggregationBuilder; +import org.opensearch.geo.search.aggregations.bucket.geogrid.GeoHashGridAggregationBuilder; +import org.opensearch.geo.tests.common.RandomGeoGenerator; +import org.opensearch.plugins.Plugin; import org.opensearch.search.aggregations.BaseAggregationTestCase; -import org.opensearch.search.aggregations.bucket.geogrid.GeoGridAggregationBuilder; -import org.opensearch.search.aggregations.bucket.geogrid.GeoHashGridAggregationBuilder; -public class GeoHashGridTests extends BaseAggregationTestCase { +import java.util.Collection; +import java.util.Collections; + +public class GeoHashGridAggregationBuilderTests extends BaseAggregationTestCase { + + protected Collection> getPlugins() { + return Collections.singletonList(GeoModulePlugin.class); + } @Override protected GeoHashGridAggregationBuilder createTestAggregatorBuilder() { @@ -55,7 +64,7 @@ protected GeoHashGridAggregationBuilder createTestAggregatorBuilder() { factory.shardSize(randomIntBetween(1, Integer.MAX_VALUE)); } if (randomBoolean()) { - factory.setGeoBoundingBox(GeoBoundingBoxTests.randomBBox()); + factory.setGeoBoundingBox(RandomGeoGenerator.randomBBox()); } return factory; } diff --git a/server/src/test/java/org/opensearch/search/aggregations/bucket/GeoTileGridTests.java b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/GeoTileGridAggregationBuilderTests.java similarity index 70% rename from server/src/test/java/org/opensearch/search/aggregations/bucket/GeoTileGridTests.java rename to modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/GeoTileGridAggregationBuilderTests.java index d54667fb4f1a6..c7c0be21273bd 100644 --- a/server/src/test/java/org/opensearch/search/aggregations/bucket/GeoTileGridTests.java +++ b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/GeoTileGridAggregationBuilderTests.java @@ -30,15 +30,24 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket; +package org.opensearch.geo.search.aggregations.bucket; -import org.opensearch.common.geo.GeoBoundingBoxTests; +import org.opensearch.geo.GeoModulePlugin; +import org.opensearch.geo.search.aggregations.bucket.geogrid.GeoGridAggregationBuilder; +import org.opensearch.geo.search.aggregations.bucket.geogrid.GeoTileGridAggregationBuilder; +import org.opensearch.geo.tests.common.RandomGeoGenerator; +import org.opensearch.plugins.Plugin; import org.opensearch.search.aggregations.BaseAggregationTestCase; -import org.opensearch.search.aggregations.bucket.geogrid.GeoGridAggregationBuilder; -import org.opensearch.search.aggregations.bucket.geogrid.GeoTileGridAggregationBuilder; -import org.opensearch.search.aggregations.bucket.geogrid.GeoTileUtils; +import org.opensearch.search.aggregations.bucket.GeoTileUtils; -public class GeoTileGridTests extends BaseAggregationTestCase { +import java.util.Collection; +import java.util.Collections; + +public class GeoTileGridAggregationBuilderTests extends BaseAggregationTestCase { + + protected Collection> getPlugins() { + return Collections.singletonList(GeoModulePlugin.class); + } @Override protected GeoTileGridAggregationBuilder createTestAggregatorBuilder() { @@ -55,7 +64,7 @@ protected GeoTileGridAggregationBuilder createTestAggregatorBuilder() { factory.shardSize(randomIntBetween(1, Integer.MAX_VALUE)); } if (randomBoolean()) { - factory.setGeoBoundingBox(GeoBoundingBoxTests.randomBBox()); + factory.setGeoBoundingBox(RandomGeoGenerator.randomBBox()); } return factory; } diff --git a/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/composite/GeoTileGridAggregationCompositeAggregatorTests.java b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/composite/GeoTileGridAggregationCompositeAggregatorTests.java new file mode 100644 index 0000000000000..3c7c292f9d193 --- /dev/null +++ b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/composite/GeoTileGridAggregationCompositeAggregatorTests.java @@ -0,0 +1,174 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.geo.search.aggregations.bucket.composite; + +import org.apache.lucene.document.Document; +import org.apache.lucene.document.LatLonPoint; +import org.apache.lucene.document.SortedNumericDocValuesField; +import org.apache.lucene.search.DocValuesFieldExistsQuery; +import org.apache.lucene.search.MatchAllDocsQuery; +import org.junit.Before; +import org.opensearch.common.geo.GeoPoint; +import org.opensearch.geo.GeoModulePlugin; +import org.opensearch.geo.search.aggregations.bucket.geogrid.GeoTileGridAggregationBuilder; +import org.opensearch.geo.search.aggregations.bucket.geogrid.GeoTileGridAggregator; +import org.opensearch.index.mapper.GeoPointFieldMapper; +import org.opensearch.plugins.SearchPlugin; +import org.opensearch.search.aggregations.bucket.composite.CompositeAggregationBuilder; +import org.opensearch.search.aggregations.bucket.GeoTileUtils; +import org.opensearch.search.aggregations.composite.BaseCompositeAggregatorTestCase; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * Testing the {@link GeoTileGridAggregator} as part of CompositeAggregation. + */ +public class GeoTileGridAggregationCompositeAggregatorTests extends BaseCompositeAggregatorTestCase { + + protected List getSearchPlugins() { + return Collections.singletonList(new GeoModulePlugin()); + } + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + FIELD_TYPES.add(new GeoPointFieldMapper.GeoPointFieldType("geo_point")); + } + + public void testUnmappedFieldWithGeopoint() throws Exception { + final List>> dataset = new ArrayList<>(); + final String mappedFieldName = "geo_point"; + dataset.addAll( + Arrays.asList( + createDocument(mappedFieldName, new GeoPoint(48.934059, 41.610741)), + createDocument(mappedFieldName, new GeoPoint(-23.065941, 113.610741)), + createDocument(mappedFieldName, new GeoPoint(90.0, 0.0)), + createDocument(mappedFieldName, new GeoPoint(37.2343, -115.8067)), + createDocument(mappedFieldName, new GeoPoint(90.0, 0.0)) + ) + ); + + // just unmapped = no results + testSearchCase( + Arrays.asList(new MatchAllDocsQuery(), new DocValuesFieldExistsQuery(mappedFieldName)), + dataset, + () -> new CompositeAggregationBuilder("name", Arrays.asList(new GeoTileGridValuesSourceBuilder("unmapped").field("unmapped"))), + (result) -> assertEquals(0, result.getBuckets().size()) + ); + + // unmapped missing bucket = one result + testSearchCase( + Arrays.asList(new MatchAllDocsQuery(), new DocValuesFieldExistsQuery(mappedFieldName)), + dataset, + () -> new CompositeAggregationBuilder( + "name", + Arrays.asList(new GeoTileGridValuesSourceBuilder("unmapped").field("unmapped").missingBucket(true)) + ), + (result) -> { + assertEquals(1, result.getBuckets().size()); + assertEquals("{unmapped=null}", result.afterKey().toString()); + assertEquals("{unmapped=null}", result.getBuckets().get(0).getKeyAsString()); + assertEquals(5L, result.getBuckets().get(0).getDocCount()); + } + ); + + // field + unmapped, no missing bucket = no results + testSearchCase( + Arrays.asList(new MatchAllDocsQuery(), new DocValuesFieldExistsQuery(mappedFieldName)), + dataset, + () -> new CompositeAggregationBuilder( + "name", + Arrays.asList( + new GeoTileGridValuesSourceBuilder(mappedFieldName).field(mappedFieldName), + new GeoTileGridValuesSourceBuilder("unmapped").field("unmapped") + ) + ), + (result) -> assertEquals(0, result.getBuckets().size()) + ); + + // field + unmapped with missing bucket = multiple results + testSearchCase( + Arrays.asList(new MatchAllDocsQuery(), new DocValuesFieldExistsQuery(mappedFieldName)), + dataset, + () -> new CompositeAggregationBuilder( + "name", + Arrays.asList( + new GeoTileGridValuesSourceBuilder(mappedFieldName).field(mappedFieldName), + new GeoTileGridValuesSourceBuilder("unmapped").field("unmapped").missingBucket(true) + ) + ), + (result) -> { + assertEquals(2, result.getBuckets().size()); + assertEquals("{geo_point=7/64/56, unmapped=null}", result.afterKey().toString()); + assertEquals("{geo_point=7/32/56, unmapped=null}", result.getBuckets().get(0).getKeyAsString()); + assertEquals(2L, result.getBuckets().get(0).getDocCount()); + assertEquals("{geo_point=7/64/56, unmapped=null}", result.getBuckets().get(1).getKeyAsString()); + assertEquals(3L, result.getBuckets().get(1).getDocCount()); + } + ); + + } + + public void testWithGeoPoint() throws Exception { + final List>> dataset = new ArrayList<>(); + dataset.addAll( + Arrays.asList( + createDocument("geo_point", new GeoPoint(48.934059, 41.610741)), + createDocument("geo_point", new GeoPoint(-23.065941, 113.610741)), + createDocument("geo_point", new GeoPoint(90.0, 0.0)), + createDocument("geo_point", new GeoPoint(37.2343, -115.8067)), + createDocument("geo_point", new GeoPoint(90.0, 0.0)) + ) + ); + testSearchCase(Arrays.asList(new MatchAllDocsQuery(), new DocValuesFieldExistsQuery("geo_point")), dataset, () -> { + GeoTileGridValuesSourceBuilder geoTile = new GeoTileGridValuesSourceBuilder("geo_point").field("geo_point"); + return new CompositeAggregationBuilder("name", Collections.singletonList(geoTile)); + }, (result) -> { + assertEquals(2, result.getBuckets().size()); + assertEquals("{geo_point=7/64/56}", result.afterKey().toString()); + assertEquals("{geo_point=7/32/56}", result.getBuckets().get(0).getKeyAsString()); + assertEquals(2L, result.getBuckets().get(0).getDocCount()); + assertEquals("{geo_point=7/64/56}", result.getBuckets().get(1).getKeyAsString()); + assertEquals(3L, result.getBuckets().get(1).getDocCount()); + }); + + testSearchCase(Arrays.asList(new MatchAllDocsQuery(), new DocValuesFieldExistsQuery("geo_point")), dataset, () -> { + GeoTileGridValuesSourceBuilder geoTile = new GeoTileGridValuesSourceBuilder("geo_point").field("geo_point"); + return new CompositeAggregationBuilder("name", Collections.singletonList(geoTile)).aggregateAfter( + Collections.singletonMap("geo_point", "7/32/56") + ); + }, (result) -> { + assertEquals(1, result.getBuckets().size()); + assertEquals("{geo_point=7/64/56}", result.afterKey().toString()); + assertEquals("{geo_point=7/64/56}", result.getBuckets().get(0).getKeyAsString()); + assertEquals(3L, result.getBuckets().get(0).getDocCount()); + }); + } + + @Override + protected boolean addValueToDocument(final Document doc, final String name, final Object value) { + if (value instanceof GeoPoint) { + GeoPoint point = (GeoPoint) value; + doc.add( + new SortedNumericDocValuesField( + name, + GeoTileUtils.longEncode(point.lon(), point.lat(), GeoTileGridAggregationBuilder.DEFAULT_PRECISION) + ) + ); + doc.add(new LatLonPoint(name, point.lat(), point.lon())); + return true; + } + return false; + } +} diff --git a/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/composite/GeoTileGridCompositeAggregationBuilderTests.java b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/composite/GeoTileGridCompositeAggregationBuilderTests.java new file mode 100644 index 0000000000000..ea7a2a83945c2 --- /dev/null +++ b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/composite/GeoTileGridCompositeAggregationBuilderTests.java @@ -0,0 +1,50 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.geo.search.aggregations.bucket.composite; + +import org.opensearch.geo.GeoModulePlugin; +import org.opensearch.geo.tests.common.RandomGeoGenerator; +import org.opensearch.plugins.Plugin; +import org.opensearch.search.aggregations.BaseAggregationTestCase; +import org.opensearch.search.aggregations.bucket.GeoTileUtils; +import org.opensearch.search.aggregations.bucket.composite.CompositeAggregationBuilder; +import org.opensearch.search.aggregations.bucket.composite.CompositeValuesSourceBuilder; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +public class GeoTileGridCompositeAggregationBuilderTests extends BaseAggregationTestCase { + + protected Collection> getPlugins() { + return Collections.singletonList(GeoModulePlugin.class); + } + + private GeoTileGridValuesSourceBuilder randomGeoTileGridValuesSourceBuilder() { + GeoTileGridValuesSourceBuilder geoTile = new GeoTileGridValuesSourceBuilder(randomAlphaOfLengthBetween(5, 10)); + if (randomBoolean()) { + geoTile.precision(randomIntBetween(0, GeoTileUtils.MAX_ZOOM)); + } + if (randomBoolean()) { + geoTile.geoBoundingBox(RandomGeoGenerator.randomBBox()); + } + return geoTile; + } + + @Override + protected CompositeAggregationBuilder createTestAggregatorBuilder() { + int numSources = randomIntBetween(1, 10); + List> sources = new ArrayList<>(); + for (int i = 0; i < numSources; i++) { + sources.add(randomGeoTileGridValuesSourceBuilder()); + } + return new CompositeAggregationBuilder(randomAlphaOfLength(10), sources); + } +} diff --git a/server/src/test/java/org/opensearch/search/aggregations/bucket/composite/GeoTileGridValuesSourceBuilderTests.java b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/composite/GeoTileGridValuesSourceBuilderTests.java similarity index 90% rename from server/src/test/java/org/opensearch/search/aggregations/bucket/composite/GeoTileGridValuesSourceBuilderTests.java rename to modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/composite/GeoTileGridValuesSourceBuilderTests.java index 2b1700676f549..c6276c06c4511 100644 --- a/server/src/test/java/org/opensearch/search/aggregations/bucket/composite/GeoTileGridValuesSourceBuilderTests.java +++ b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/composite/GeoTileGridValuesSourceBuilderTests.java @@ -30,8 +30,9 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.composite; +package org.opensearch.geo.search.aggregations.bucket.composite; +import org.opensearch.search.aggregations.bucket.composite.CompositeValuesSourceBuilder; import org.opensearch.test.OpenSearchTestCase; public class GeoTileGridValuesSourceBuilderTests extends OpenSearchTestCase { diff --git a/server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoGridAggregatorTestCase.java b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoGridAggregatorTestCase.java similarity index 95% rename from server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoGridAggregatorTestCase.java rename to modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoGridAggregatorTestCase.java index 17fddb8978499..d6153637f656d 100644 --- a/server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoGridAggregatorTestCase.java +++ b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoGridAggregatorTestCase.java @@ -29,7 +29,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.apache.lucene.document.LatLonDocValuesField; import org.apache.lucene.document.SortedSetDocValuesField; @@ -45,17 +45,19 @@ import org.apache.lucene.util.BytesRef; import org.opensearch.common.CheckedConsumer; import org.opensearch.common.geo.GeoBoundingBox; -import org.opensearch.common.geo.GeoBoundingBoxTests; import org.opensearch.common.geo.GeoUtils; +import org.opensearch.geo.GeoModulePlugin; +import org.opensearch.geo.tests.common.AggregationInspectionHelper; +import org.opensearch.geo.tests.common.RandomGeoGenerator; import org.opensearch.index.mapper.GeoPointFieldMapper; import org.opensearch.index.mapper.MappedFieldType; +import org.opensearch.plugins.SearchPlugin; import org.opensearch.search.aggregations.Aggregation; import org.opensearch.search.aggregations.Aggregator; import org.opensearch.search.aggregations.AggregatorTestCase; import org.opensearch.search.aggregations.MultiBucketConsumerService; import org.opensearch.search.aggregations.bucket.terms.StringTerms; import org.opensearch.search.aggregations.bucket.terms.TermsAggregationBuilder; -import org.opensearch.search.aggregations.support.AggregationInspectionHelper; import java.io.IOException; import java.util.ArrayList; @@ -91,6 +93,16 @@ public abstract class GeoGridAggregatorTestCase */ protected abstract GeoGridAggregationBuilder createBuilder(String name); + /** + * Overriding the Search Plugins list with {@link GeoModulePlugin} so that the testcase will know that this plugin is + * to be loaded during the tests. + * @return List of {@link SearchPlugin} + */ + @Override + protected List getSearchPlugins() { + return Collections.singletonList(new GeoModulePlugin()); + } + public void testNoDocs() throws IOException { testCase( new MatchAllDocsQuery(), @@ -225,7 +237,7 @@ public void testBounds() throws IOException { // only consider bounding boxes that are at least GEOHASH_TOLERANCE wide and have quantized coordinates GeoBoundingBox bbox = randomValueOtherThanMany( (b) -> Math.abs(GeoUtils.normalizeLon(b.right()) - GeoUtils.normalizeLon(b.left())) < GEOHASH_TOLERANCE, - GeoBoundingBoxTests::randomBBox + RandomGeoGenerator::randomBBox ); Function encodeDecodeLat = (lat) -> GeoEncodingUtils.decodeLatitude(GeoEncodingUtils.encodeLatitude(lat)); Function encodeDecodeLon = (lon) -> GeoEncodingUtils.decodeLongitude(GeoEncodingUtils.encodeLongitude(lon)); diff --git a/server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoGridTestCase.java b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoGridTestCase.java similarity index 79% rename from server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoGridTestCase.java rename to modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoGridTestCase.java index ce286a4443660..432736a2b43fe 100644 --- a/server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoGridTestCase.java +++ b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoGridTestCase.java @@ -29,9 +29,16 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.apache.lucene.index.IndexWriter; +import org.opensearch.common.ParseField; +import org.opensearch.common.xcontent.ContextParser; +import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.geo.GeoModulePlugin; +import org.opensearch.geo.search.aggregations.metrics.ParsedGeoBounds; +import org.opensearch.plugins.SearchPlugin; +import org.opensearch.search.aggregations.Aggregation; import org.opensearch.search.aggregations.InternalAggregations; import org.opensearch.search.aggregations.ParsedMultiBucketAggregation; import org.opensearch.test.InternalMultiBucketAggregationTestCase; @@ -76,6 +83,36 @@ protected int maxNumberOfBuckets() { return 3; } + /** + * Overriding the method so that tests can get the aggregation specs for namedWriteable. + * + * @return GeoPlugin + */ + @Override + protected SearchPlugin registerPlugin() { + return new GeoModulePlugin(); + } + + /** + * Overriding with the {@link ParsedGeoBounds} so that it can be parsed. We need to do this as {@link GeoModulePlugin} + * is registering this Aggregation. + * + * @return a List of {@link NamedXContentRegistry.Entry} + */ + @Override + protected List getNamedXContents() { + final List namedXContents = new ArrayList<>(getDefaultNamedXContents()); + final ContextParser hashGridParser = (p, c) -> ParsedGeoHashGrid.fromXContent(p, (String) c); + final ContextParser geoTileParser = (p, c) -> ParsedGeoTileGrid.fromXContent(p, (String) c); + namedXContents.add( + new NamedXContentRegistry.Entry(Aggregation.class, new ParseField(GeoHashGridAggregationBuilder.NAME), hashGridParser) + ); + namedXContents.add( + new NamedXContentRegistry.Entry(Aggregation.class, new ParseField(GeoTileGridAggregationBuilder.NAME), geoTileParser) + ); + return namedXContents; + } + @Override protected T createTestInstance(String name, Map metadata, InternalAggregations aggregations) { final int precision = randomPrecision(); diff --git a/server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoHashGridAggregatorTests.java b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoHashGridAggregatorTests.java similarity index 96% rename from server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoHashGridAggregatorTests.java rename to modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoHashGridAggregatorTests.java index 5c63b15c7f614..04fa815366f6b 100644 --- a/server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoHashGridAggregatorTests.java +++ b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoHashGridAggregatorTests.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import static org.opensearch.geometry.utils.Geohash.stringEncode; diff --git a/server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoHashGridParserTests.java b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoHashGridParserTests.java similarity index 99% rename from server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoHashGridParserTests.java rename to modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoHashGridParserTests.java index e81e22b3b562f..44f292e898a61 100644 --- a/server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoHashGridParserTests.java +++ b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoHashGridParserTests.java @@ -29,7 +29,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.greaterThanOrEqualTo; diff --git a/server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoHashGridTests.java b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoHashGridTests.java similarity index 97% rename from server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoHashGridTests.java rename to modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoHashGridTests.java index 5a26ec759281c..c84c6ef5ec076 100644 --- a/server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoHashGridTests.java +++ b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoHashGridTests.java @@ -29,7 +29,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.geometry.utils.Geohash; import org.opensearch.search.aggregations.InternalAggregations; diff --git a/server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileGridAggregatorTests.java b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoTileGridAggregatorTests.java similarity index 94% rename from server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileGridAggregatorTests.java rename to modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoTileGridAggregatorTests.java index 4e88111ac2dfc..f2f641ea794c0 100644 --- a/server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileGridAggregatorTests.java +++ b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoTileGridAggregatorTests.java @@ -30,7 +30,9 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; + +import org.opensearch.search.aggregations.bucket.GeoTileUtils; public class GeoTileGridAggregatorTests extends GeoGridAggregatorTestCase { diff --git a/server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileGridParserTests.java b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoTileGridParserTests.java similarity index 97% rename from server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileGridParserTests.java rename to modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoTileGridParserTests.java index 567bcd57d23e5..a5b000d5e6ab3 100644 --- a/server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileGridParserTests.java +++ b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoTileGridParserTests.java @@ -29,7 +29,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.ExceptionsHelper; import org.opensearch.common.xcontent.XContentParseException; @@ -37,6 +37,7 @@ import org.opensearch.common.xcontent.json.JsonXContent; import org.opensearch.geo.GeometryTestUtils; import org.opensearch.geometry.Rectangle; +import org.opensearch.search.aggregations.bucket.GeoTileUtils; import org.opensearch.test.OpenSearchTestCase; import static org.hamcrest.Matchers.containsString; diff --git a/server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileGridTests.java b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoTileGridTests.java similarity index 94% rename from server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileGridTests.java rename to modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoTileGridTests.java index 50b9a8cd762d1..ead67e0455d94 100644 --- a/server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileGridTests.java +++ b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoTileGridTests.java @@ -29,9 +29,10 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.search.aggregations.InternalAggregations; +import org.opensearch.search.aggregations.bucket.GeoTileUtils; import java.util.List; import java.util.Map; diff --git a/modules/geo/src/test/java/org/opensearch/geo/tests/common/AggregationBuilders.java b/modules/geo/src/test/java/org/opensearch/geo/tests/common/AggregationBuilders.java index c1f27b71c326d..c0d7e51047c6b 100644 --- a/modules/geo/src/test/java/org/opensearch/geo/tests/common/AggregationBuilders.java +++ b/modules/geo/src/test/java/org/opensearch/geo/tests/common/AggregationBuilders.java @@ -8,6 +8,10 @@ package org.opensearch.geo.tests.common; +import org.opensearch.geo.search.aggregations.bucket.geogrid.GeoHashGridAggregationBuilder; +import org.opensearch.geo.search.aggregations.bucket.geogrid.GeoTileGridAggregationBuilder; +import org.opensearch.geo.search.aggregations.bucket.geogrid.InternalGeoHashGrid; +import org.opensearch.geo.search.aggregations.bucket.geogrid.InternalGeoTileGrid; import org.opensearch.geo.search.aggregations.metrics.GeoBounds; import org.opensearch.geo.search.aggregations.metrics.GeoBoundsAggregationBuilder; @@ -18,4 +22,18 @@ public class AggregationBuilders { public static GeoBoundsAggregationBuilder geoBounds(String name) { return new GeoBoundsAggregationBuilder(name); } + + /** + * Create a new {@link InternalGeoHashGrid} aggregation with the given name. + */ + public static GeoHashGridAggregationBuilder geohashGrid(String name) { + return new GeoHashGridAggregationBuilder(name); + } + + /** + * Create a new {@link InternalGeoTileGrid} aggregation with the given name. + */ + public static GeoTileGridAggregationBuilder geotileGrid(String name) { + return new GeoTileGridAggregationBuilder(name); + } } diff --git a/modules/geo/src/test/java/org/opensearch/geo/tests/common/AggregationInspectionHelper.java b/modules/geo/src/test/java/org/opensearch/geo/tests/common/AggregationInspectionHelper.java index 208187bf34a5c..3473cf2d94b76 100644 --- a/modules/geo/src/test/java/org/opensearch/geo/tests/common/AggregationInspectionHelper.java +++ b/modules/geo/src/test/java/org/opensearch/geo/tests/common/AggregationInspectionHelper.java @@ -8,6 +8,7 @@ package org.opensearch.geo.tests.common; +import org.opensearch.geo.search.aggregations.bucket.geogrid.InternalGeoGrid; import org.opensearch.geo.search.aggregations.metrics.InternalGeoBounds; public class AggregationInspectionHelper { @@ -15,4 +16,8 @@ public class AggregationInspectionHelper { public static boolean hasValue(InternalGeoBounds agg) { return (agg.topLeft() == null && agg.bottomRight() == null) == false; } + + public static boolean hasValue(InternalGeoGrid agg) { + return agg.getBuckets().stream().anyMatch(bucket -> bucket.getDocCount() > 0); + } } diff --git a/modules/geo/src/test/java/org/opensearch/geo/tests/common/RandomGeoGenerator.java b/modules/geo/src/test/java/org/opensearch/geo/tests/common/RandomGeoGenerator.java index 2cf32c36b97ec..2fb403155e2bc 100644 --- a/modules/geo/src/test/java/org/opensearch/geo/tests/common/RandomGeoGenerator.java +++ b/modules/geo/src/test/java/org/opensearch/geo/tests/common/RandomGeoGenerator.java @@ -8,7 +8,10 @@ package org.opensearch.geo.tests.common; +import org.opensearch.common.geo.GeoBoundingBox; import org.opensearch.common.geo.GeoPoint; +import org.opensearch.geo.GeometryTestUtils; +import org.opensearch.geometry.Rectangle; import java.util.Random; @@ -83,4 +86,12 @@ private static double normalizeLongitude(double longitude) { return -180 + off; } } + + public static GeoBoundingBox randomBBox() { + Rectangle rectangle = GeometryTestUtils.randomRectangle(); + return new GeoBoundingBox( + new GeoPoint(rectangle.getMaxLat(), rectangle.getMinLon()), + new GeoPoint(rectangle.getMinLat(), rectangle.getMaxLon()) + ); + } } diff --git a/modules/geo/src/yamlRestTest/resources/rest-api-spec/test/geo_shape/230_composite.yml b/modules/geo/src/yamlRestTest/resources/rest-api-spec/test/geo_shape/230_composite.yml new file mode 100644 index 0000000000000..211f3c3f46b88 --- /dev/null +++ b/modules/geo/src/yamlRestTest/resources/rest-api-spec/test/geo_shape/230_composite.yml @@ -0,0 +1,168 @@ +--- +setup: + - do: + indices.create: + index: test + body: + mappings: + properties: + date: + type: date + keyword: + type: keyword + long: + type: long + geo_point: + type: geo_point + nested: + type: nested + properties: + nested_long: + type: long + + - do: + indices.create: + index: other + body: + mappings: + properties: + date: + type: date + long: + type: long + nested: + type: nested + properties: + nested_long: + type: long + + - do: + index: + index: test + id: 1 + body: { "keyword": "foo", "long": [10, 20], "geo_point": "37.2343,-115.8067", "nested": [{"nested_long": 10}, {"nested_long": 20}] } + + - do: + index: + index: test + id: 2 + body: { "keyword": ["foo", "bar"], "geo_point": "41.12,-71.34" } + + - do: + index: + index: test + id: 3 + body: { "keyword": "bar", "long": [100, 0], "geo_point": "90.0,0.0", "nested": [{"nested_long": 10}, {"nested_long": 0}] } + + - do: + index: + index: test + id: 4 + body: { "keyword": "bar", "long": [1000, 0], "geo_point": "41.12,-71.34", "nested": [{"nested_long": 1000}, {"nested_long": 20}] } + + - do: + index: + index: test + id: 5 + body: { "date": "2017-10-20T03:08:45" } + + - do: + index: + index: test + id: 6 + body: { "date": "2017-10-21T07:00:00" } + + - do: + index: + index: other + id: 0 + body: { "date": "2017-10-20T03:08:45" } + + - do: + indices.refresh: + index: [test, other] +--- +"Simple Composite aggregation with GeoTile grid": + - skip: + version: " - 7.4.99" + reason: geotile_grid is not supported until 7.5.0 + - do: + search: + rest_total_hits_as_int: true + index: test + body: + aggregations: + test: + composite: + sources: [ + "geo": { + "geotile_grid": { + "field": "geo_point", + "precision": 12 + } + }, + { + "kw": { + "terms": { + "field": "keyword" + } + } + } + ] + + - match: {hits.total: 6} + - length: { aggregations.test.buckets: 4 } + - match: { aggregations.test.buckets.0.key.geo: "12/730/1590" } + - match: { aggregations.test.buckets.0.key.kw: "foo" } + - match: { aggregations.test.buckets.0.doc_count: 1 } + - match: { aggregations.test.buckets.1.key.geo: "12/1236/1533" } + - match: { aggregations.test.buckets.1.key.kw: "bar" } + - match: { aggregations.test.buckets.1.doc_count: 2 } + - match: { aggregations.test.buckets.2.key.geo: "12/1236/1533" } + - match: { aggregations.test.buckets.2.key.kw: "foo" } + - match: { aggregations.test.buckets.2.doc_count: 1 } + - match: { aggregations.test.buckets.3.key.geo: "12/2048/0" } + - match: { aggregations.test.buckets.3.key.kw: "bar" } + - match: { aggregations.test.buckets.3.doc_count: 1 } + +--- +"Simple Composite aggregation with geotile grid add aggregate after": + - skip: + version: " - 7.4.99" + reason: geotile_grid is not supported until 7.5.0 + - do: + search: + index: test + body: + aggregations: + test: + composite: + sources: [ + "geo": { + "geotile_grid": { + "field": "geo_point", + "precision": 12 + } + }, + { + "kw": { + "terms": { + "field": "keyword" + } + } + } + ] + after: { "geo": "12/730/1590", "kw": "foo" } + + - match: { hits.total.value: 6 } + - match: { hits.total.relation: "eq" } + - length: { aggregations.test.buckets: 3 } + - match: { aggregations.test.buckets.0.key.geo: "12/1236/1533" } + - match: { aggregations.test.buckets.0.key.kw: "bar" } + - match: { aggregations.test.buckets.0.doc_count: 2 } + - match: { aggregations.test.buckets.1.key.geo: "12/1236/1533" } + - match: { aggregations.test.buckets.1.key.kw: "foo" } + - match: { aggregations.test.buckets.1.doc_count: 1 } + - match: { aggregations.test.buckets.2.key.geo: "12/2048/0" } + - match: { aggregations.test.buckets.2.key.kw: "bar" } + - match: { aggregations.test.buckets.2.doc_count: 1 } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/280_geohash_grid.yml b/modules/geo/src/yamlRestTest/resources/rest-api-spec/test/geo_shape/280_geohash_grid.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/280_geohash_grid.yml rename to modules/geo/src/yamlRestTest/resources/rest-api-spec/test/geo_shape/280_geohash_grid.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/290_geotile_grid.yml b/modules/geo/src/yamlRestTest/resources/rest-api-spec/test/geo_shape/290_geotile_grid.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/290_geotile_grid.yml rename to modules/geo/src/yamlRestTest/resources/rest-api-spec/test/geo_shape/290_geotile_grid.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/230_composite.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/230_composite.yml index 2e298441918bc..09278690f5d05 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/230_composite.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/230_composite.yml @@ -651,92 +651,6 @@ setup: } ] ---- -"Simple Composite aggregation with GeoTile grid": - - skip: - version: " - 7.4.99" - reason: geotile_grid is not supported until 7.5.0 - - do: - search: - rest_total_hits_as_int: true - index: test - body: - aggregations: - test: - composite: - sources: [ - "geo": { - "geotile_grid": { - "field": "geo_point", - "precision": 12 - } - }, - { - "kw": { - "terms": { - "field": "keyword" - } - } - } - ] - - - match: {hits.total: 6} - - length: { aggregations.test.buckets: 4 } - - match: { aggregations.test.buckets.0.key.geo: "12/730/1590" } - - match: { aggregations.test.buckets.0.key.kw: "foo" } - - match: { aggregations.test.buckets.0.doc_count: 1 } - - match: { aggregations.test.buckets.1.key.geo: "12/1236/1533" } - - match: { aggregations.test.buckets.1.key.kw: "bar" } - - match: { aggregations.test.buckets.1.doc_count: 2 } - - match: { aggregations.test.buckets.2.key.geo: "12/1236/1533" } - - match: { aggregations.test.buckets.2.key.kw: "foo" } - - match: { aggregations.test.buckets.2.doc_count: 1 } - - match: { aggregations.test.buckets.3.key.geo: "12/2048/0" } - - match: { aggregations.test.buckets.3.key.kw: "bar" } - - match: { aggregations.test.buckets.3.doc_count: 1 } - ---- -"Simple Composite aggregation with geotile grid add aggregate after": - - skip: - version: " - 7.4.99" - reason: geotile_grid is not supported until 7.5.0 - - do: - search: - index: test - body: - aggregations: - test: - composite: - sources: [ - "geo": { - "geotile_grid": { - "field": "geo_point", - "precision": 12 - } - }, - { - "kw": { - "terms": { - "field": "keyword" - } - } - } - ] - after: { "geo": "12/730/1590", "kw": "foo" } - - - match: { hits.total.value: 6 } - - match: { hits.total.relation: "eq" } - - length: { aggregations.test.buckets: 3 } - - match: { aggregations.test.buckets.0.key.geo: "12/1236/1533" } - - match: { aggregations.test.buckets.0.key.kw: "bar" } - - match: { aggregations.test.buckets.0.doc_count: 2 } - - match: { aggregations.test.buckets.1.key.geo: "12/1236/1533" } - - match: { aggregations.test.buckets.1.key.kw: "foo" } - - match: { aggregations.test.buckets.1.doc_count: 1 } - - match: { aggregations.test.buckets.2.key.geo: "12/2048/0" } - - match: { aggregations.test.buckets.2.key.kw: "bar" } - - match: { aggregations.test.buckets.2.doc_count: 1 } - --- "Mixed ip and unmapped fields": - skip: diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/ShardReduceIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/ShardReduceIT.java index 7352dc7170a21..faa6a54394b00 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/ShardReduceIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/ShardReduceIT.java @@ -37,7 +37,6 @@ import org.opensearch.index.query.QueryBuilders; import org.opensearch.search.aggregations.Aggregator.SubAggCollectionMode; import org.opensearch.search.aggregations.bucket.filter.Filter; -import org.opensearch.search.aggregations.bucket.geogrid.GeoGrid; import org.opensearch.search.aggregations.bucket.global.Global; import org.opensearch.search.aggregations.bucket.histogram.DateHistogramInterval; import org.opensearch.search.aggregations.bucket.histogram.Histogram; @@ -51,8 +50,6 @@ import static org.opensearch.search.aggregations.AggregationBuilders.dateHistogram; import static org.opensearch.search.aggregations.AggregationBuilders.dateRange; import static org.opensearch.search.aggregations.AggregationBuilders.filter; -import static org.opensearch.search.aggregations.AggregationBuilders.geohashGrid; -import static org.opensearch.search.aggregations.AggregationBuilders.geotileGrid; import static org.opensearch.search.aggregations.AggregationBuilders.global; import static org.opensearch.search.aggregations.AggregationBuilders.histogram; import static org.opensearch.search.aggregations.AggregationBuilders.ipRange; @@ -338,36 +335,4 @@ public void testDateHistogram() throws Exception { } - public void testGeoHashGrid() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .setQuery(QueryBuilders.matchAllQuery()) - .addAggregation( - geohashGrid("grid").field("location") - .subAggregation(dateHistogram("histo").field("date").dateHistogramInterval(DateHistogramInterval.DAY).minDocCount(0)) - ) - .get(); - - assertSearchResponse(response); - - GeoGrid grid = response.getAggregations().get("grid"); - Histogram histo = grid.getBuckets().iterator().next().getAggregations().get("histo"); - assertThat(histo.getBuckets().size(), equalTo(4)); - } - - public void testGeoTileGrid() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .setQuery(QueryBuilders.matchAllQuery()) - .addAggregation( - geotileGrid("grid").field("location") - .subAggregation(dateHistogram("histo").field("date").dateHistogramInterval(DateHistogramInterval.DAY).minDocCount(0)) - ) - .get(); - - assertSearchResponse(response); - - GeoGrid grid = response.getAggregations().get("grid"); - Histogram histo = grid.getBuckets().iterator().next().getAggregations().get("histo"); - assertThat(histo.getBuckets().size(), equalTo(4)); - } - } diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/GeoCentroidIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/GeoCentroidIT.java index 7cd8b3ed39051..ffc31b7cdb7c4 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/GeoCentroidIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/GeoCentroidIT.java @@ -35,15 +35,11 @@ import org.opensearch.action.search.SearchResponse; import org.opensearch.common.geo.GeoPoint; import org.opensearch.search.aggregations.InternalAggregation; -import org.opensearch.search.aggregations.bucket.geogrid.GeoGrid; import org.opensearch.search.aggregations.bucket.global.Global; import org.opensearch.test.OpenSearchIntegTestCase; -import java.util.List; - import static org.opensearch.index.query.QueryBuilders.matchAllQuery; import static org.opensearch.search.aggregations.AggregationBuilders.geoCentroid; -import static org.opensearch.search.aggregations.AggregationBuilders.geohashGrid; import static org.opensearch.search.aggregations.AggregationBuilders.global; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertSearchResponse; import static org.hamcrest.Matchers.closeTo; @@ -168,33 +164,4 @@ public void testMultiValuedField() throws Exception { assertThat(centroid.lon(), closeTo(multiCentroid.lon(), GEOHASH_TOLERANCE)); assertEquals(2 * numDocs, geoCentroid.count()); } - - public void testSingleValueFieldAsSubAggToGeohashGrid() throws Exception { - SearchResponse response = client().prepareSearch(HIGH_CARD_IDX_NAME) - .addAggregation( - geohashGrid("geoGrid").field(SINGLE_VALUED_FIELD_NAME).subAggregation(geoCentroid(aggName).field(SINGLE_VALUED_FIELD_NAME)) - ) - .get(); - assertSearchResponse(response); - - GeoGrid grid = response.getAggregations().get("geoGrid"); - assertThat(grid, notNullValue()); - assertThat(grid.getName(), equalTo("geoGrid")); - List buckets = grid.getBuckets(); - for (GeoGrid.Bucket cell : buckets) { - String geohash = cell.getKeyAsString(); - GeoPoint expectedCentroid = expectedCentroidsForGeoHash.get(geohash); - GeoCentroid centroidAgg = cell.getAggregations().get(aggName); - assertThat( - "Geohash " + geohash + " has wrong centroid latitude ", - expectedCentroid.lat(), - closeTo(centroidAgg.centroid().lat(), GEOHASH_TOLERANCE) - ); - assertThat( - "Geohash " + geohash + " has wrong centroid longitude", - expectedCentroid.lon(), - closeTo(centroidAgg.centroid().lon(), GEOHASH_TOLERANCE) - ); - } - } } diff --git a/server/src/main/java/org/opensearch/plugins/SearchPlugin.java b/server/src/main/java/org/opensearch/plugins/SearchPlugin.java index a743360e1e90c..af7d4fc2e9fe5 100644 --- a/server/src/main/java/org/opensearch/plugins/SearchPlugin.java +++ b/server/src/main/java/org/opensearch/plugins/SearchPlugin.java @@ -55,6 +55,9 @@ import org.opensearch.search.aggregations.Aggregator; import org.opensearch.search.aggregations.InternalAggregation; import org.opensearch.search.aggregations.PipelineAggregationBuilder; +import org.opensearch.search.aggregations.bucket.composite.CompositeAggregation; +import org.opensearch.search.aggregations.bucket.composite.CompositeAggregationParsingFunction; +import org.opensearch.search.aggregations.bucket.composite.CompositeValuesSourceBuilder; import org.opensearch.search.aggregations.bucket.terms.SignificantTerms; import org.opensearch.search.aggregations.bucket.terms.heuristic.SignificanceHeuristic; import org.opensearch.search.aggregations.pipeline.MovAvgModel; @@ -172,6 +175,15 @@ default List> getAggregationExtentions() return emptyList(); } + /** + * Allows plugins to register new Aggregation in the {@link CompositeAggregation}. + * + * @return A {@link List} of {@link CompositeAggregationSpec} + */ + default List getCompositeAggregations() { + return emptyList(); + } + /** * The new {@link PipelineAggregator}s added by this plugin. */ @@ -532,6 +544,76 @@ public AggregationSpec setAggregatorRegistrar(Consumer aggregatorRegistrar; + private final Class valueSourceBuilderClass; + @Deprecated + /** This is added for backward compatibility, you don't need to set it, as we use aggregationType instead of + * byte code + */ + private Byte byteCode; + private final CompositeAggregationParsingFunction parsingFunction; + private final String aggregationType; + private final Writeable.Reader> reader; + + /** + * Specification for registering an aggregation in Composite Aggregation + * + * @param aggregatorRegistrar function to register the + * {@link org.opensearch.search.aggregations.support.ValuesSource} to aggregator mappings for Composite + * aggregation + * @param valueSourceBuilderClass ValueSourceBuilder class name which is building the aggregation + * @param byteCode byte code which is used in serialisation and de-serialisation to indentify which + * aggregation builder to use + * @param reader Typically, a reference to a constructor that takes a {@link StreamInput}, which is + * registered with the aggregation + * @param parsingFunction a reference function which will be used to parse the Aggregation input. + * @param aggregationType a {@link String} defined in the AggregationBuilder as type. + */ + public CompositeAggregationSpec( + final Consumer aggregatorRegistrar, + final Class> valueSourceBuilderClass, + final Byte byteCode, + final Writeable.Reader> reader, + final CompositeAggregationParsingFunction parsingFunction, + final String aggregationType + ) { + this.aggregatorRegistrar = aggregatorRegistrar; + this.valueSourceBuilderClass = valueSourceBuilderClass; + this.byteCode = byteCode; + this.parsingFunction = parsingFunction; + this.aggregationType = aggregationType; + this.reader = reader; + } + + public Consumer getAggregatorRegistrar() { + return aggregatorRegistrar; + } + + public Class getValueSourceBuilderClass() { + return valueSourceBuilderClass; + } + + public Byte getByteCode() { + return byteCode; + } + + public CompositeAggregationParsingFunction getParsingFunction() { + return parsingFunction; + } + + public String getAggregationType() { + return aggregationType; + } + + public Writeable.Reader> getReader() { + return reader; + } + } + /** * Specification for a {@link PipelineAggregator}. */ diff --git a/server/src/main/java/org/opensearch/search/DocValueFormat.java b/server/src/main/java/org/opensearch/search/DocValueFormat.java index 7e7e4f83334f5..84c46e400543a 100644 --- a/server/src/main/java/org/opensearch/search/DocValueFormat.java +++ b/server/src/main/java/org/opensearch/search/DocValueFormat.java @@ -47,7 +47,7 @@ import org.opensearch.common.time.DateUtils; import org.opensearch.geometry.utils.Geohash; import org.opensearch.index.mapper.DateFieldMapper; -import org.opensearch.search.aggregations.bucket.geogrid.GeoTileUtils; +import org.opensearch.search.aggregations.bucket.GeoTileUtils; import java.io.IOException; import java.math.BigInteger; diff --git a/server/src/main/java/org/opensearch/search/SearchModule.java b/server/src/main/java/org/opensearch/search/SearchModule.java index 80e025a3651a8..0149f9a025bcd 100644 --- a/server/src/main/java/org/opensearch/search/SearchModule.java +++ b/server/src/main/java/org/opensearch/search/SearchModule.java @@ -126,10 +126,6 @@ import org.opensearch.search.aggregations.bucket.filter.FiltersAggregationBuilder; import org.opensearch.search.aggregations.bucket.filter.InternalFilter; import org.opensearch.search.aggregations.bucket.filter.InternalFilters; -import org.opensearch.search.aggregations.bucket.geogrid.GeoHashGridAggregationBuilder; -import org.opensearch.search.aggregations.bucket.geogrid.GeoTileGridAggregationBuilder; -import org.opensearch.search.aggregations.bucket.geogrid.InternalGeoHashGrid; -import org.opensearch.search.aggregations.bucket.geogrid.InternalGeoTileGrid; import org.opensearch.search.aggregations.bucket.global.GlobalAggregationBuilder; import org.opensearch.search.aggregations.bucket.global.InternalGlobal; import org.opensearch.search.aggregations.bucket.histogram.AutoDateHistogramAggregationBuilder; @@ -628,22 +624,6 @@ private ValuesSourceRegistry registerAggregations(List plugins) { ).addResultReader(InternalGeoDistance::new).setAggregatorRegistrar(GeoDistanceAggregationBuilder::registerAggregators), builder ); - registerAggregation( - new AggregationSpec( - GeoHashGridAggregationBuilder.NAME, - GeoHashGridAggregationBuilder::new, - GeoHashGridAggregationBuilder.PARSER - ).addResultReader(InternalGeoHashGrid::new).setAggregatorRegistrar(GeoHashGridAggregationBuilder::registerAggregators), - builder - ); - registerAggregation( - new AggregationSpec( - GeoTileGridAggregationBuilder.NAME, - GeoTileGridAggregationBuilder::new, - GeoTileGridAggregationBuilder.PARSER - ).addResultReader(InternalGeoTileGrid::new).setAggregatorRegistrar(GeoTileGridAggregationBuilder::registerAggregators), - builder - ); registerAggregation( new AggregationSpec(NestedAggregationBuilder.NAME, NestedAggregationBuilder::new, NestedAggregationBuilder::parse) .addResultReader(InternalNested::new), @@ -681,7 +661,7 @@ private ValuesSourceRegistry registerAggregations(List plugins) { registerAggregation( new AggregationSpec(CompositeAggregationBuilder.NAME, CompositeAggregationBuilder::new, CompositeAggregationBuilder.PARSER) .addResultReader(InternalComposite::new) - .setAggregatorRegistrar(CompositeAggregationBuilder::registerAggregators), + .setAggregatorRegistrar(reg -> CompositeAggregationBuilder.registerAggregators(reg, plugins)), builder ); registerAggregation( diff --git a/server/src/main/java/org/opensearch/search/aggregations/AggregationBuilders.java b/server/src/main/java/org/opensearch/search/aggregations/AggregationBuilders.java index 382455093309d..9886e423bbc76 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/AggregationBuilders.java +++ b/server/src/main/java/org/opensearch/search/aggregations/AggregationBuilders.java @@ -43,10 +43,6 @@ import org.opensearch.search.aggregations.bucket.filter.Filters; import org.opensearch.search.aggregations.bucket.filter.FiltersAggregationBuilder; import org.opensearch.search.aggregations.bucket.filter.FiltersAggregator.KeyedFilter; -import org.opensearch.search.aggregations.bucket.geogrid.GeoHashGridAggregationBuilder; -import org.opensearch.search.aggregations.bucket.geogrid.GeoTileGridAggregationBuilder; -import org.opensearch.search.aggregations.bucket.geogrid.InternalGeoHashGrid; -import org.opensearch.search.aggregations.bucket.geogrid.InternalGeoTileGrid; import org.opensearch.search.aggregations.bucket.global.Global; import org.opensearch.search.aggregations.bucket.global.GlobalAggregationBuilder; import org.opensearch.search.aggregations.bucket.histogram.DateHistogramAggregationBuilder; @@ -261,20 +257,6 @@ public static HistogramAggregationBuilder histogram(String name) { return new HistogramAggregationBuilder(name); } - /** - * Create a new {@link InternalGeoHashGrid} aggregation with the given name. - */ - public static GeoHashGridAggregationBuilder geohashGrid(String name) { - return new GeoHashGridAggregationBuilder(name); - } - - /** - * Create a new {@link InternalGeoTileGrid} aggregation with the given name. - */ - public static GeoTileGridAggregationBuilder geotileGrid(String name) { - return new GeoTileGridAggregationBuilder(name); - } - /** * Create a new {@link SignificantTerms} aggregation with the given name. */ diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileUtils.java b/server/src/main/java/org/opensearch/search/aggregations/bucket/GeoTileUtils.java similarity index 97% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileUtils.java rename to server/src/main/java/org/opensearch/search/aggregations/bucket/GeoTileUtils.java index 5498b2b1a7109..6cd1823622f01 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileUtils.java +++ b/server/src/main/java/org/opensearch/search/aggregations/bucket/GeoTileUtils.java @@ -29,7 +29,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.search.aggregations.bucket; import org.apache.lucene.geo.GeoEncodingUtils; import org.apache.lucene.util.SloppyMath; @@ -104,7 +104,7 @@ private GeoTileUtils() {} * @param parser {@link XContentParser} to parse the value from * @return int representing precision */ - static int parsePrecision(XContentParser parser) throws IOException, OpenSearchParseException { + public static int parsePrecision(XContentParser parser) throws IOException, OpenSearchParseException { final Object node = parser.currentToken().equals(XContentParser.Token.VALUE_NUMBER) ? Integer.valueOf(parser.intValue()) : parser.text(); @@ -252,7 +252,7 @@ public static String stringEncode(long hash) { /** * Decode long hash as a GeoPoint (center of the tile) */ - static GeoPoint hashToGeoPoint(long hash) { + public static GeoPoint hashToGeoPoint(long hash) { int[] res = parseHash(hash); return zxyToGeoPoint(res[0], res[1], res[2]); } @@ -260,7 +260,7 @@ static GeoPoint hashToGeoPoint(long hash) { /** * Decode a string bucket key in "zoom/x/y" format to a GeoPoint (center of the tile) */ - static GeoPoint keyToGeoPoint(String hashAsString) { + public static GeoPoint keyToGeoPoint(String hashAsString) { int[] hashAsInts = parseHash(hashAsString); return zxyToGeoPoint(hashAsInts[0], hashAsInts[1], hashAsInts[2]); } diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/composite/CompositeAggregationBuilder.java b/server/src/main/java/org/opensearch/search/aggregations/bucket/composite/CompositeAggregationBuilder.java index 8b07df3f689bf..093c2ad42722e 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/composite/CompositeAggregationBuilder.java +++ b/server/src/main/java/org/opensearch/search/aggregations/bucket/composite/CompositeAggregationBuilder.java @@ -35,9 +35,11 @@ import org.opensearch.common.ParseField; import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.common.io.stream.Writeable; import org.opensearch.common.xcontent.ConstructingObjectParser; import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.index.query.QueryShardContext; +import org.opensearch.plugins.SearchPlugin; import org.opensearch.search.aggregations.AbstractAggregationBuilder; import org.opensearch.search.aggregations.AggregationBuilder; import org.opensearch.search.aggregations.AggregatorFactories; @@ -47,11 +49,14 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Function; import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; @@ -82,14 +87,55 @@ public class CompositeAggregationBuilder extends AbstractAggregationBuilder p.map(), AFTER_FIELD_NAME); } - public static void registerAggregators(ValuesSourceRegistry.Builder builder) { + static final Map, Byte> BUILDER_CLASS_TO_BYTE_CODE = new HashMap<>(); + static final Map BUILDER_TYPE_TO_PARSER = new HashMap<>(); + static final Map>> BYTE_CODE_TO_COMPOSITE_VALUE_SOURCE_READER = + new HashMap<>(); + static final Map< + String, + Writeable.Reader>> AGGREGATION_TYPE_TO_COMPOSITE_VALUE_SOURCE_READER = new HashMap<>(); + static final Map, String> BUILDER_CLASS_TO_AGGREGATION_TYPE = new HashMap<>(); + + public static void registerAggregators(ValuesSourceRegistry.Builder builder, final List plugins) { DateHistogramValuesSourceBuilder.register(builder); HistogramValuesSourceBuilder.register(builder); - GeoTileGridValuesSourceBuilder.register(builder); TermsValuesSourceBuilder.register(builder); + // Register All other aggregations that wants to be part of Composite Aggregation which are provided in + // Plugins along with their parsers and serialisation codes + registerCompositeAggregatorsPlugins(plugins, SearchPlugin::getCompositeAggregations, (compositeAggregationSpec) -> { + compositeAggregationSpec.getAggregatorRegistrar().accept(builder); + BUILDER_TYPE_TO_PARSER.put(compositeAggregationSpec.getAggregationType(), compositeAggregationSpec.getParsingFunction()); + // This is added for backward compatibility, so that we can move away from byte code in the serialisation + if (compositeAggregationSpec.getByteCode() != null) { + BYTE_CODE_TO_COMPOSITE_VALUE_SOURCE_READER.put( + (int) compositeAggregationSpec.getByteCode(), + compositeAggregationSpec.getReader() + ); + BUILDER_CLASS_TO_BYTE_CODE.put( + compositeAggregationSpec.getValueSourceBuilderClass(), + compositeAggregationSpec.getByteCode() + ); + } + AGGREGATION_TYPE_TO_COMPOSITE_VALUE_SOURCE_READER.put( + compositeAggregationSpec.getAggregationType(), + compositeAggregationSpec.getReader() + ); + BUILDER_CLASS_TO_AGGREGATION_TYPE.put( + compositeAggregationSpec.getValueSourceBuilderClass(), + compositeAggregationSpec.getAggregationType() + ); + }); builder.registerUsage(NAME); } + private static void registerCompositeAggregatorsPlugins( + final List plugins, + final Function> producer, + final Consumer consumer + ) { + plugins.forEach(searchPlugin -> producer.apply(searchPlugin).forEach(consumer)); + } + private List> sources; private Map after; private int size = 10; diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/composite/CompositeAggregationParsingFunction.java b/server/src/main/java/org/opensearch/search/aggregations/bucket/composite/CompositeAggregationParsingFunction.java new file mode 100644 index 0000000000000..344563ad20309 --- /dev/null +++ b/server/src/main/java/org/opensearch/search/aggregations/bucket/composite/CompositeAggregationParsingFunction.java @@ -0,0 +1,22 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.search.aggregations.bucket.composite; + +import org.opensearch.common.xcontent.XContentParser; + +import java.io.IOException; + +/** + * A functional interface which encapsulates the parsing function to be called for the aggregation which is + * also registered as CompositeAggregation. + */ +@FunctionalInterface +public interface CompositeAggregationParsingFunction { + CompositeValuesSourceBuilder parse(final String name, final XContentParser parser) throws IOException; +} diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/composite/CompositeValuesSourceBuilder.java b/server/src/main/java/org/opensearch/search/aggregations/bucket/composite/CompositeValuesSourceBuilder.java index 7764d367a0cec..26015ae04cf76 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/composite/CompositeValuesSourceBuilder.java +++ b/server/src/main/java/org/opensearch/search/aggregations/bucket/composite/CompositeValuesSourceBuilder.java @@ -69,11 +69,11 @@ public abstract class CompositeValuesSourceBuilder createValuesSource( * @param missingBucket If true an explicit null bucket will represent documents with missing values. * @param hasScript true if the source contains a script that can change the value. */ - CompositeValuesSourceConfig( + public CompositeValuesSourceConfig( String name, @Nullable MappedFieldType fieldType, ValuesSource vs, @@ -113,21 +113,21 @@ SingleDimensionValuesSource createValuesSource( /** * Returns the name associated with this configuration. */ - String name() { + protected String name() { return name; } /** * Returns the {@link MappedFieldType} for this config. */ - MappedFieldType fieldType() { + public MappedFieldType fieldType() { return fieldType; } /** * Returns the {@link ValuesSource} for this configuration. */ - ValuesSource valuesSource() { + public ValuesSource valuesSource() { return vs; } @@ -135,35 +135,35 @@ ValuesSource valuesSource() { * The {@link DocValueFormat} to use for formatting the keys. * {@link DocValueFormat#RAW} means no formatting. */ - DocValueFormat format() { + public DocValueFormat format() { return format; } /** * If true, an explicit `null bucket represents documents with missing values. */ - boolean missingBucket() { + public boolean missingBucket() { return missingBucket; } /** * Return the {@link MissingOrder} for the config. */ - MissingOrder missingOrder() { + public MissingOrder missingOrder() { return missingOrder; } /** * Returns true if the source contains a script that can change the value. */ - boolean hasScript() { + protected boolean hasScript() { return hasScript; } /** * The sort order for the values source (e.g. -1 for descending and 1 for ascending). */ - int reverseMul() { + public int reverseMul() { assert reverseMul == -1 || reverseMul == 1; return reverseMul; } diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/composite/CompositeValuesSourceParserHelper.java b/server/src/main/java/org/opensearch/search/aggregations/bucket/composite/CompositeValuesSourceParserHelper.java index 60d7f277f7650..d8526e684f391 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/composite/CompositeValuesSourceParserHelper.java +++ b/server/src/main/java/org/opensearch/search/aggregations/bucket/composite/CompositeValuesSourceParserHelper.java @@ -49,6 +49,11 @@ import java.io.IOException; import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken; +import static org.opensearch.search.aggregations.bucket.composite.CompositeAggregationBuilder.AGGREGATION_TYPE_TO_COMPOSITE_VALUE_SOURCE_READER; +import static org.opensearch.search.aggregations.bucket.composite.CompositeAggregationBuilder.BUILDER_CLASS_TO_AGGREGATION_TYPE; +import static org.opensearch.search.aggregations.bucket.composite.CompositeAggregationBuilder.BUILDER_CLASS_TO_BYTE_CODE; +import static org.opensearch.search.aggregations.bucket.composite.CompositeAggregationBuilder.BUILDER_TYPE_TO_PARSER; +import static org.opensearch.search.aggregations.bucket.composite.CompositeAggregationBuilder.BYTE_CODE_TO_COMPOSITE_VALUE_SOURCE_READER; /** * Helper class for obtaining values source parsers for different aggs @@ -57,7 +62,11 @@ */ public class CompositeValuesSourceParserHelper { - static , T> void declareValuesSourceFields(AbstractObjectParser objectParser) { + private static final int AGGREGATION_TYPE_REFERENCE = Byte.MAX_VALUE; + + public static , T> void declareValuesSourceFields( + AbstractObjectParser objectParser + ) { objectParser.declareField(VB::field, XContentParser::text, new ParseField("field"), ObjectParser.ValueType.STRING); objectParser.declareBoolean(VB::missingBucket, new ParseField("missing_bucket")); objectParser.declareString(VB::missingOrder, new ParseField(MissingOrder.NAME)); @@ -78,28 +87,45 @@ static , T> void declareValuesSource } public static void writeTo(CompositeValuesSourceBuilder builder, StreamOutput out) throws IOException { - final byte code; + int code = Byte.MIN_VALUE; + String aggregationType = null; if (builder.getClass() == TermsValuesSourceBuilder.class) { code = 0; } else if (builder.getClass() == DateHistogramValuesSourceBuilder.class) { code = 1; } else if (builder.getClass() == HistogramValuesSourceBuilder.class) { code = 2; - } else if (builder.getClass() == GeoTileGridValuesSourceBuilder.class) { - if (out.getVersion().before(LegacyESVersion.V_7_5_0)) { - throw new IOException( - "Attempting to serialize [" - + builder.getClass().getSimpleName() - + "] to a node with unsupported version [" - + out.getVersion() - + "]" - ); - } - code = 3; } else { - throw new IOException("invalid builder type: " + builder.getClass().getSimpleName()); + if (!BUILDER_CLASS_TO_BYTE_CODE.containsKey(builder.getClass()) + && !BUILDER_CLASS_TO_AGGREGATION_TYPE.containsKey(builder.getClass())) { + throw new IOException("invalid builder type: " + builder.getClass().getSimpleName()); + } + aggregationType = BUILDER_CLASS_TO_AGGREGATION_TYPE.get(builder.getClass()); + if (BUILDER_CLASS_TO_BYTE_CODE.containsKey(builder.getClass())) { + code = BUILDER_CLASS_TO_BYTE_CODE.get(builder.getClass()); + if (code == 3 && out.getVersion().before(LegacyESVersion.V_7_5_0)) { + throw new IOException( + "Attempting to serialize [" + + builder.getClass().getSimpleName() + + "] to a node with unsupported version [" + + out.getVersion() + + "]" + ); + } + } + } + + if (code != Byte.MIN_VALUE) { + out.writeByte((byte) code); + } else if (!BUILDER_CLASS_TO_BYTE_CODE.containsKey(builder.getClass())) { + /* + * This is added for backward compatibility when 1 data node is using the new code which is using the + * aggregation type and another is using the only byte code in the serialisation. + */ + out.writeByte((byte) AGGREGATION_TYPE_REFERENCE); + assert aggregationType != null; + out.writeString(aggregationType); } - out.writeByte(code); builder.writeTo(out); } @@ -112,10 +138,17 @@ public static CompositeValuesSourceBuilder readFrom(StreamInput in) throws IO return new DateHistogramValuesSourceBuilder(in); case 2: return new HistogramValuesSourceBuilder(in); - case 3: - return new GeoTileGridValuesSourceBuilder(in); + case AGGREGATION_TYPE_REFERENCE: + final String aggregationType = in.readString(); + if (!AGGREGATION_TYPE_TO_COMPOSITE_VALUE_SOURCE_READER.containsKey(aggregationType)) { + throw new IOException("Invalid aggregation type " + aggregationType); + } + return (CompositeValuesSourceBuilder) AGGREGATION_TYPE_TO_COMPOSITE_VALUE_SOURCE_READER.get(aggregationType).read(in); default: - throw new IOException("Invalid code " + code); + if (!BYTE_CODE_TO_COMPOSITE_VALUE_SOURCE_READER.containsKey(code)) { + throw new IOException("Invalid code " + code); + } + return (CompositeValuesSourceBuilder) BYTE_CODE_TO_COMPOSITE_VALUE_SOURCE_READER.get(code).read(in); } } @@ -143,11 +176,11 @@ public static CompositeValuesSourceBuilder fromXContent(XContentParser parser case HistogramValuesSourceBuilder.TYPE: builder = HistogramValuesSourceBuilder.parse(name, parser); break; - case GeoTileGridValuesSourceBuilder.TYPE: - builder = GeoTileGridValuesSourceBuilder.parse(name, parser); - break; default: - throw new ParsingException(parser.getTokenLocation(), "invalid source type: " + type); + if (!BUILDER_TYPE_TO_PARSER.containsKey(type)) { + throw new ParsingException(parser.getTokenLocation(), "invalid source type: " + type); + } + builder = BUILDER_TYPE_TO_PARSER.get(type).parse(name, parser); } parser.nextToken(); parser.nextToken(); @@ -163,4 +196,5 @@ public static XContentBuilder toXContent(CompositeValuesSourceBuilder source, builder.endObject(); return builder; } + } diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/composite/LongValuesSource.java b/server/src/main/java/org/opensearch/search/aggregations/bucket/composite/LongValuesSource.java index a7ed50507288d..ec6410c2a9377 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/composite/LongValuesSource.java +++ b/server/src/main/java/org/opensearch/search/aggregations/bucket/composite/LongValuesSource.java @@ -66,7 +66,7 @@ * * @opensearch.internal */ -class LongValuesSource extends SingleDimensionValuesSource { +public class LongValuesSource extends SingleDimensionValuesSource { private final BigArrays bigArrays; private final CheckedFunction docValuesFunc; private final LongUnaryOperator rounding; @@ -76,7 +76,7 @@ class LongValuesSource extends SingleDimensionValuesSource { private long currentValue; private boolean missingCurrentValue; - LongValuesSource( + public LongValuesSource( BigArrays bigArrays, MappedFieldType fieldType, CheckedFunction docValuesFunc, @@ -165,7 +165,7 @@ private int compareValues(long v1, long v2) { } @Override - void setAfter(Comparable value) { + protected void setAfter(Comparable value) { if (missingBucket && value == null) { afterValue = null; } else { diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/composite/SingleDimensionValuesSource.java b/server/src/main/java/org/opensearch/search/aggregations/bucket/composite/SingleDimensionValuesSource.java index 747a7017ec872..fe0801d6d230e 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/composite/SingleDimensionValuesSource.java +++ b/server/src/main/java/org/opensearch/search/aggregations/bucket/composite/SingleDimensionValuesSource.java @@ -53,7 +53,7 @@ * * @opensearch.internal */ -abstract class SingleDimensionValuesSource> implements Releasable { +public abstract class SingleDimensionValuesSource> implements Releasable { protected final BigArrays bigArrays; protected final DocValueFormat format; @Nullable diff --git a/server/src/main/java/org/opensearch/search/aggregations/support/AggregationInspectionHelper.java b/server/src/main/java/org/opensearch/search/aggregations/support/AggregationInspectionHelper.java index f36c4620d5b33..b4da1d10b4b68 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/support/AggregationInspectionHelper.java +++ b/server/src/main/java/org/opensearch/search/aggregations/support/AggregationInspectionHelper.java @@ -35,7 +35,6 @@ import org.opensearch.search.aggregations.bucket.composite.InternalComposite; import org.opensearch.search.aggregations.bucket.filter.InternalFilter; import org.opensearch.search.aggregations.bucket.filter.InternalFilters; -import org.opensearch.search.aggregations.bucket.geogrid.InternalGeoGrid; import org.opensearch.search.aggregations.bucket.global.InternalGlobal; import org.opensearch.search.aggregations.bucket.histogram.InternalVariableWidthHistogram; import org.opensearch.search.aggregations.bucket.histogram.InternalAutoDateHistogram; @@ -119,10 +118,6 @@ public static boolean hasValue(InternalFilter agg) { return agg.getDocCount() > 0; } - public static boolean hasValue(InternalGeoGrid agg) { - return agg.getBuckets().stream().anyMatch(bucket -> bucket.getDocCount() > 0); - } - public static boolean hasValue(InternalGlobal agg) { return agg.getDocCount() > 0; } diff --git a/server/src/main/java/org/opensearch/search/aggregations/support/package-info.java b/server/src/main/java/org/opensearch/search/aggregations/support/package-info.java index e16e8c91b3fd0..dd2c16f1daa0e 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/support/package-info.java +++ b/server/src/main/java/org/opensearch/search/aggregations/support/package-info.java @@ -43,7 +43,7 @@ * output). A class hierarchy defines the type of values returned by the source. The top level sub-classes define type-specific behavior, * such as {@link org.opensearch.search.aggregations.support.ValuesSource.Numeric#isFloatingPoint()}. Second level subclasses are * then specialized based on where they read values from, e.g. script or field cases. There are also adapter classes like - * {@link org.opensearch.search.aggregations.bucket.geogrid.CellIdSource} which do run-time conversion from one type to another, often + * org.opensearch.search.aggregations.bucket.geogrid.CellIdSource which do run-time conversion from one type to another, often * dependent on a user specified parameter (precision in that case). *

* diff --git a/server/src/test/java/org/opensearch/search/DocValueFormatTests.java b/server/src/test/java/org/opensearch/search/DocValueFormatTests.java index 36a6eb3ae87b0..bd0fbfe69960c 100644 --- a/server/src/test/java/org/opensearch/search/DocValueFormatTests.java +++ b/server/src/test/java/org/opensearch/search/DocValueFormatTests.java @@ -48,7 +48,7 @@ import java.util.ArrayList; import java.util.List; -import static org.opensearch.search.aggregations.bucket.geogrid.GeoTileUtils.longEncode; +import static org.opensearch.search.aggregations.bucket.GeoTileUtils.longEncode; public class DocValueFormatTests extends OpenSearchTestCase { diff --git a/server/src/test/java/org/opensearch/search/aggregations/AggregationsTests.java b/server/src/test/java/org/opensearch/search/aggregations/AggregationsTests.java index 111ce23f8a0cb..94fb6cded637d 100644 --- a/server/src/test/java/org/opensearch/search/aggregations/AggregationsTests.java +++ b/server/src/test/java/org/opensearch/search/aggregations/AggregationsTests.java @@ -48,8 +48,6 @@ import org.opensearch.search.aggregations.bucket.composite.InternalCompositeTests; import org.opensearch.search.aggregations.bucket.filter.InternalFilterTests; import org.opensearch.search.aggregations.bucket.filter.InternalFiltersTests; -import org.opensearch.search.aggregations.bucket.geogrid.GeoHashGridTests; -import org.opensearch.search.aggregations.bucket.geogrid.GeoTileGridTests; import org.opensearch.search.aggregations.bucket.global.InternalGlobalTests; import org.opensearch.search.aggregations.bucket.histogram.InternalAutoDateHistogramTests; import org.opensearch.search.aggregations.bucket.histogram.InternalDateHistogramTests; @@ -157,8 +155,6 @@ private static List> getAggsTests() { aggsTests.add(new InternalGlobalTests()); aggsTests.add(new InternalFilterTests()); aggsTests.add(new InternalSamplerTests()); - aggsTests.add(new GeoHashGridTests()); - aggsTests.add(new GeoTileGridTests()); aggsTests.add(new InternalRangeTests()); aggsTests.add(new InternalDateRangeTests()); aggsTests.add(new InternalGeoDistanceTests()); diff --git a/server/src/test/java/org/opensearch/search/aggregations/bucket/composite/CompositeAggregationBuilderTests.java b/server/src/test/java/org/opensearch/search/aggregations/bucket/composite/CompositeAggregationBuilderTests.java index c4a87f3993bb4..9290183ec7312 100644 --- a/server/src/test/java/org/opensearch/search/aggregations/bucket/composite/CompositeAggregationBuilderTests.java +++ b/server/src/test/java/org/opensearch/search/aggregations/bucket/composite/CompositeAggregationBuilderTests.java @@ -32,10 +32,8 @@ package org.opensearch.search.aggregations.bucket.composite; -import org.opensearch.common.geo.GeoBoundingBoxTests; import org.opensearch.script.Script; import org.opensearch.search.aggregations.BaseAggregationTestCase; -import org.opensearch.search.aggregations.bucket.geogrid.GeoTileUtils; import org.opensearch.search.aggregations.bucket.histogram.DateHistogramInterval; import org.opensearch.search.aggregations.bucket.missing.MissingOrder; import org.opensearch.search.sort.SortOrder; @@ -74,17 +72,6 @@ private DateHistogramValuesSourceBuilder randomDateHistogramSourceBuilder() { return histo; } - private GeoTileGridValuesSourceBuilder randomGeoTileGridValuesSourceBuilder() { - GeoTileGridValuesSourceBuilder geoTile = new GeoTileGridValuesSourceBuilder(randomAlphaOfLengthBetween(5, 10)); - if (randomBoolean()) { - geoTile.precision(randomIntBetween(0, GeoTileUtils.MAX_ZOOM)); - } - if (randomBoolean()) { - geoTile.geoBoundingBox(GeoBoundingBoxTests.randomBBox()); - } - return geoTile; - } - private TermsValuesSourceBuilder randomTermsSourceBuilder() { TermsValuesSourceBuilder terms = new TermsValuesSourceBuilder(randomAlphaOfLengthBetween(5, 10)); if (randomBoolean()) { @@ -118,11 +105,9 @@ private HistogramValuesSourceBuilder randomHistogramSourceBuilder() { @Override protected CompositeAggregationBuilder createTestAggregatorBuilder() { int numSources = randomIntBetween(1, 10); - numSources = 1; List> sources = new ArrayList<>(); for (int i = 0; i < numSources; i++) { - int type = randomIntBetween(0, 3); - type = 3; + int type = randomIntBetween(0, 2); switch (type) { case 0: sources.add(randomTermsSourceBuilder()); @@ -133,9 +118,6 @@ protected CompositeAggregationBuilder createTestAggregatorBuilder() { case 2: sources.add(randomHistogramSourceBuilder()); break; - case 3: - sources.add(randomGeoTileGridValuesSourceBuilder()); - break; default: throw new AssertionError("wrong branch"); } diff --git a/server/src/test/java/org/opensearch/search/aggregations/bucket/composite/CompositeAggregatorTests.java b/server/src/test/java/org/opensearch/search/aggregations/bucket/composite/CompositeAggregatorTests.java index 88b2323b8adfc..25003e0b84567 100644 --- a/server/src/test/java/org/opensearch/search/aggregations/bucket/composite/CompositeAggregatorTests.java +++ b/server/src/test/java/org/opensearch/search/aggregations/bucket/composite/CompositeAggregatorTests.java @@ -32,68 +32,24 @@ package org.opensearch.search.aggregations.bucket.composite; -import org.apache.lucene.tests.analysis.MockAnalyzer; -import org.apache.lucene.document.Document; -import org.apache.lucene.document.DoublePoint; -import org.apache.lucene.document.Field; -import org.apache.lucene.document.InetAddressPoint; -import org.apache.lucene.document.IntPoint; -import org.apache.lucene.document.LatLonPoint; import org.apache.lucene.document.LongPoint; -import org.apache.lucene.document.SortedNumericDocValuesField; -import org.apache.lucene.document.SortedSetDocValuesField; -import org.apache.lucene.document.StringField; -import org.apache.lucene.index.DirectoryReader; -import org.apache.lucene.index.IndexReader; -import org.apache.lucene.index.IndexWriterConfig; -import org.apache.lucene.tests.index.RandomIndexWriter; import org.apache.lucene.index.Term; import org.apache.lucene.search.DocValuesFieldExistsQuery; -import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.MatchAllDocsQuery; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.Sort; -import org.apache.lucene.search.SortField; -import org.apache.lucene.search.SortedNumericSortField; -import org.apache.lucene.search.SortedSetSortField; import org.apache.lucene.search.TermQuery; -import org.apache.lucene.store.Directory; -import org.apache.lucene.util.BytesRef; -import org.apache.lucene.util.NumericUtils; -import org.apache.lucene.tests.util.TestUtil; import org.opensearch.OpenSearchParseException; -import org.opensearch.common.geo.GeoPoint; -import org.opensearch.common.settings.Settings; -import org.opensearch.common.text.Text; -import org.opensearch.common.time.DateFormatter; -import org.opensearch.common.time.DateFormatters; -import org.opensearch.index.Index; -import org.opensearch.index.IndexSettings; -import org.opensearch.index.mapper.DateFieldMapper; -import org.opensearch.index.mapper.DocumentMapper; -import org.opensearch.index.mapper.GeoPointFieldMapper; -import org.opensearch.index.mapper.IpFieldMapper; -import org.opensearch.index.mapper.KeywordFieldMapper; -import org.opensearch.index.mapper.MappedFieldType; -import org.opensearch.index.mapper.MapperService; -import org.opensearch.index.mapper.NumberFieldMapper; import org.opensearch.search.aggregations.Aggregator; -import org.opensearch.search.aggregations.AggregatorTestCase; -import org.opensearch.search.aggregations.bucket.geogrid.GeoTileGridAggregationBuilder; -import org.opensearch.search.aggregations.bucket.geogrid.GeoTileUtils; import org.opensearch.search.aggregations.bucket.histogram.DateHistogramInterval; import org.opensearch.search.aggregations.bucket.missing.MissingOrder; import org.opensearch.search.aggregations.bucket.terms.StringTerms; import org.opensearch.search.aggregations.bucket.terms.TermsAggregationBuilder; +import org.opensearch.search.aggregations.composite.BaseCompositeAggregatorTestCase; import org.opensearch.search.aggregations.metrics.InternalMax; import org.opensearch.search.aggregations.metrics.MaxAggregationBuilder; import org.opensearch.search.aggregations.metrics.TopHits; import org.opensearch.search.aggregations.metrics.TopHitsAggregationBuilder; import org.opensearch.search.aggregations.support.ValueType; import org.opensearch.search.sort.SortOrder; -import org.opensearch.test.IndexSettingsModule; -import org.junit.After; -import org.junit.Before; import java.io.IOException; import java.net.InetAddress; @@ -109,51 +65,14 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; -import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; -import java.util.stream.Collectors; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class CompositeAggregatorTests extends AggregatorTestCase { - private static MappedFieldType[] FIELD_TYPES; - - @Override - @Before - public void setUp() throws Exception { - super.setUp(); - FIELD_TYPES = new MappedFieldType[8]; - FIELD_TYPES[0] = new KeywordFieldMapper.KeywordFieldType("keyword"); - FIELD_TYPES[1] = new NumberFieldMapper.NumberFieldType("long", NumberFieldMapper.NumberType.LONG); - FIELD_TYPES[2] = new NumberFieldMapper.NumberFieldType("double", NumberFieldMapper.NumberType.DOUBLE); - FIELD_TYPES[3] = new DateFieldMapper.DateFieldType("date", DateFormatter.forPattern("yyyy-MM-dd||epoch_millis")); - FIELD_TYPES[4] = new NumberFieldMapper.NumberFieldType("price", NumberFieldMapper.NumberType.INTEGER); - FIELD_TYPES[5] = new KeywordFieldMapper.KeywordFieldType("terms"); - FIELD_TYPES[6] = new IpFieldMapper.IpFieldType("ip"); - FIELD_TYPES[7] = new GeoPointFieldMapper.GeoPointFieldType("geo_point"); - } - - @Override - @After - public void tearDown() throws Exception { - super.tearDown(); - FIELD_TYPES = null; - } - @Override - protected MapperService mapperServiceMock() { - MapperService mapperService = mock(MapperService.class); - DocumentMapper mapper = mock(DocumentMapper.class); - when(mapper.typeText()).thenReturn(new Text("_doc")); - when(mapper.type()).thenReturn("_doc"); - when(mapperService.documentMapper()).thenReturn(mapper); - return mapperService; - } +public class CompositeAggregatorTests extends BaseCompositeAggregatorTestCase { public void testUnmappedFieldWithTerms() throws Exception { final List>> dataset = new ArrayList<>(); @@ -234,80 +153,6 @@ public void testUnmappedFieldWithTerms() throws Exception { ); } - public void testUnmappedFieldWithGeopoint() throws Exception { - final List>> dataset = new ArrayList<>(); - final String mappedFieldName = "geo_point"; - dataset.addAll( - Arrays.asList( - createDocument(mappedFieldName, new GeoPoint(48.934059, 41.610741)), - createDocument(mappedFieldName, new GeoPoint(-23.065941, 113.610741)), - createDocument(mappedFieldName, new GeoPoint(90.0, 0.0)), - createDocument(mappedFieldName, new GeoPoint(37.2343, -115.8067)), - createDocument(mappedFieldName, new GeoPoint(90.0, 0.0)) - ) - ); - - // just unmapped = no results - testSearchCase( - Arrays.asList(new MatchAllDocsQuery(), new DocValuesFieldExistsQuery(mappedFieldName)), - dataset, - () -> new CompositeAggregationBuilder("name", Arrays.asList(new GeoTileGridValuesSourceBuilder("unmapped").field("unmapped"))), - (result) -> assertEquals(0, result.getBuckets().size()) - ); - - // unmapped missing bucket = one result - testSearchCase( - Arrays.asList(new MatchAllDocsQuery(), new DocValuesFieldExistsQuery(mappedFieldName)), - dataset, - () -> new CompositeAggregationBuilder( - "name", - Arrays.asList(new GeoTileGridValuesSourceBuilder("unmapped").field("unmapped").missingBucket(true)) - ), - (result) -> { - assertEquals(1, result.getBuckets().size()); - assertEquals("{unmapped=null}", result.afterKey().toString()); - assertEquals("{unmapped=null}", result.getBuckets().get(0).getKeyAsString()); - assertEquals(5L, result.getBuckets().get(0).getDocCount()); - } - ); - - // field + unmapped, no missing bucket = no results - testSearchCase( - Arrays.asList(new MatchAllDocsQuery(), new DocValuesFieldExistsQuery(mappedFieldName)), - dataset, - () -> new CompositeAggregationBuilder( - "name", - Arrays.asList( - new GeoTileGridValuesSourceBuilder(mappedFieldName).field(mappedFieldName), - new GeoTileGridValuesSourceBuilder("unmapped").field("unmapped") - ) - ), - (result) -> assertEquals(0, result.getBuckets().size()) - ); - - // field + unmapped with missing bucket = multiple results - testSearchCase( - Arrays.asList(new MatchAllDocsQuery(), new DocValuesFieldExistsQuery(mappedFieldName)), - dataset, - () -> new CompositeAggregationBuilder( - "name", - Arrays.asList( - new GeoTileGridValuesSourceBuilder(mappedFieldName).field(mappedFieldName), - new GeoTileGridValuesSourceBuilder("unmapped").field("unmapped").missingBucket(true) - ) - ), - (result) -> { - assertEquals(2, result.getBuckets().size()); - assertEquals("{geo_point=7/64/56, unmapped=null}", result.afterKey().toString()); - assertEquals("{geo_point=7/32/56, unmapped=null}", result.getBuckets().get(0).getKeyAsString()); - assertEquals(2L, result.getBuckets().get(0).getDocCount()); - assertEquals("{geo_point=7/64/56, unmapped=null}", result.getBuckets().get(1).getKeyAsString()); - assertEquals(3L, result.getBuckets().get(1).getDocCount()); - } - ); - - } - public void testUnmappedFieldWithHistogram() throws Exception { final List>> dataset = new ArrayList<>(); final String mappedFieldName = "price"; @@ -2483,42 +2328,6 @@ public void testWithIP() throws Exception { }); } - public void testWithGeoPoint() throws Exception { - final List>> dataset = new ArrayList<>(); - dataset.addAll( - Arrays.asList( - createDocument("geo_point", new GeoPoint(48.934059, 41.610741)), - createDocument("geo_point", new GeoPoint(-23.065941, 113.610741)), - createDocument("geo_point", new GeoPoint(90.0, 0.0)), - createDocument("geo_point", new GeoPoint(37.2343, -115.8067)), - createDocument("geo_point", new GeoPoint(90.0, 0.0)) - ) - ); - testSearchCase(Arrays.asList(new MatchAllDocsQuery(), new DocValuesFieldExistsQuery("geo_point")), dataset, () -> { - GeoTileGridValuesSourceBuilder geoTile = new GeoTileGridValuesSourceBuilder("geo_point").field("geo_point"); - return new CompositeAggregationBuilder("name", Collections.singletonList(geoTile)); - }, (result) -> { - assertEquals(2, result.getBuckets().size()); - assertEquals("{geo_point=7/64/56}", result.afterKey().toString()); - assertEquals("{geo_point=7/32/56}", result.getBuckets().get(0).getKeyAsString()); - assertEquals(2L, result.getBuckets().get(0).getDocCount()); - assertEquals("{geo_point=7/64/56}", result.getBuckets().get(1).getKeyAsString()); - assertEquals(3L, result.getBuckets().get(1).getDocCount()); - }); - - testSearchCase(Arrays.asList(new MatchAllDocsQuery(), new DocValuesFieldExistsQuery("geo_point")), dataset, () -> { - GeoTileGridValuesSourceBuilder geoTile = new GeoTileGridValuesSourceBuilder("geo_point").field("geo_point"); - return new CompositeAggregationBuilder("name", Collections.singletonList(geoTile)).aggregateAfter( - Collections.singletonMap("geo_point", "7/32/56") - ); - }, (result) -> { - assertEquals(1, result.getBuckets().size()); - assertEquals("{geo_point=7/64/56}", result.afterKey().toString()); - assertEquals("{geo_point=7/64/56}", result.getBuckets().get(0).getKeyAsString()); - assertEquals(3L, result.getBuckets().get(0).getDocCount()); - }); - } - public void testEarlyTermination() throws Exception { final List>> dataset = new ArrayList<>(); dataset.addAll( @@ -2648,193 +2457,4 @@ public void testIndexSortWithDuplicate() throws Exception { ); } } - - private void testSearchCase( - List queries, - List>> dataset, - Supplier create, - Consumer verify - ) throws IOException { - for (Query query : queries) { - executeTestCase(false, false, query, dataset, create, verify); - executeTestCase(false, true, query, dataset, create, verify); - } - } - - private void executeTestCase( - boolean forceMerge, - boolean useIndexSort, - Query query, - List>> dataset, - Supplier create, - Consumer verify - ) throws IOException { - Map types = Arrays.stream(FIELD_TYPES) - .collect(Collectors.toMap(MappedFieldType::name, Function.identity())); - CompositeAggregationBuilder aggregationBuilder = create.get(); - Sort indexSort = useIndexSort ? buildIndexSort(aggregationBuilder.sources(), types) : null; - IndexSettings indexSettings = createIndexSettings(indexSort); - try (Directory directory = newDirectory()) { - IndexWriterConfig config = newIndexWriterConfig(random(), new MockAnalyzer(random())); - if (indexSort != null) { - config.setIndexSort(indexSort); - config.setCodec(TestUtil.getDefaultCodec()); - } - try (RandomIndexWriter indexWriter = new RandomIndexWriter(random(), directory, config)) { - Document document = new Document(); - int id = 0; - for (Map> fields : dataset) { - document.clear(); - addToDocument(id, document, fields); - indexWriter.addDocument(document); - id++; - } - if (forceMerge || rarely()) { - // forceMerge randomly or if the collector-per-leaf testing stuff would break the tests. - indexWriter.forceMerge(1); - } else { - if (dataset.size() > 0) { - int numDeletes = randomIntBetween(1, 25); - for (int i = 0; i < numDeletes; i++) { - id = randomIntBetween(0, dataset.size() - 1); - indexWriter.deleteDocuments(new Term("id", Integer.toString(id))); - document.clear(); - addToDocument(id, document, dataset.get(id)); - indexWriter.addDocument(document); - } - } - - } - } - try (IndexReader indexReader = DirectoryReader.open(directory)) { - IndexSearcher indexSearcher = new IndexSearcher(indexReader); - InternalComposite composite = searchAndReduce(indexSettings, indexSearcher, query, aggregationBuilder, FIELD_TYPES); - verify.accept(composite); - } - } - } - - private static IndexSettings createIndexSettings(Sort sort) { - Settings.Builder builder = Settings.builder(); - if (sort != null) { - String[] fields = Arrays.stream(sort.getSort()).map(SortField::getField).toArray(String[]::new); - String[] orders = Arrays.stream(sort.getSort()).map((o) -> o.getReverse() ? "desc" : "asc").toArray(String[]::new); - builder.putList("index.sort.field", fields); - builder.putList("index.sort.order", orders); - } - return IndexSettingsModule.newIndexSettings(new Index("_index", "0"), builder.build()); - } - - private void addToDocument(int id, Document doc, Map> keys) { - doc.add(new StringField("id", Integer.toString(id), Field.Store.NO)); - for (Map.Entry> entry : keys.entrySet()) { - final String name = entry.getKey(); - for (Object value : entry.getValue()) { - if (value instanceof Integer) { - doc.add(new SortedNumericDocValuesField(name, (int) value)); - doc.add(new IntPoint(name, (int) value)); - } else if (value instanceof Long) { - doc.add(new SortedNumericDocValuesField(name, (long) value)); - doc.add(new LongPoint(name, (long) value)); - } else if (value instanceof Double) { - doc.add(new SortedNumericDocValuesField(name, NumericUtils.doubleToSortableLong((double) value))); - doc.add(new DoublePoint(name, (double) value)); - } else if (value instanceof String) { - doc.add(new SortedSetDocValuesField(name, new BytesRef((String) value))); - doc.add(new StringField(name, new BytesRef((String) value), Field.Store.NO)); - } else if (value instanceof InetAddress) { - doc.add(new SortedSetDocValuesField(name, new BytesRef(InetAddressPoint.encode((InetAddress) value)))); - doc.add(new InetAddressPoint(name, (InetAddress) value)); - } else if (value instanceof GeoPoint) { - GeoPoint point = (GeoPoint) value; - doc.add( - new SortedNumericDocValuesField( - name, - GeoTileUtils.longEncode(point.lon(), point.lat(), GeoTileGridAggregationBuilder.DEFAULT_PRECISION) - ) - ); - doc.add(new LatLonPoint(name, point.lat(), point.lon())); - } else { - throw new AssertionError("invalid object: " + value.getClass().getSimpleName()); - } - } - } - } - - private static Map createAfterKey(Object... fields) { - assert fields.length % 2 == 0; - final Map map = new HashMap<>(); - for (int i = 0; i < fields.length; i += 2) { - String field = (String) fields[i]; - map.put(field, fields[i + 1]); - } - return map; - } - - @SuppressWarnings("unchecked") - private static Map> createDocument(Object... fields) { - assert fields.length % 2 == 0; - final Map> map = new HashMap<>(); - for (int i = 0; i < fields.length; i += 2) { - String field = (String) fields[i]; - if (fields[i + 1] instanceof List) { - map.put(field, (List) fields[i + 1]); - } else { - map.put(field, Collections.singletonList(fields[i + 1])); - } - } - return map; - } - - private static long asLong(String dateTime) { - return DateFormatters.from(DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER.parse(dateTime)).toInstant().toEpochMilli(); - } - - private static Sort buildIndexSort(List> sources, Map fieldTypes) { - List sortFields = new ArrayList<>(); - Map remainingFieldTypes = new HashMap<>(fieldTypes); - for (CompositeValuesSourceBuilder source : sources) { - MappedFieldType type = fieldTypes.remove(source.field()); - remainingFieldTypes.remove(source.field()); - SortField sortField = sortFieldFrom(type); - if (sortField == null) { - break; - } - sortFields.add(sortField); - } - while (remainingFieldTypes.size() > 0 && randomBoolean()) { - // Add extra unused sorts - List fields = new ArrayList<>(remainingFieldTypes.keySet()); - Collections.sort(fields); - String field = fields.get(between(0, fields.size() - 1)); - SortField sortField = sortFieldFrom(remainingFieldTypes.remove(field)); - if (sortField != null) { - sortFields.add(sortField); - } - } - return sortFields.size() > 0 ? new Sort(sortFields.toArray(new SortField[0])) : null; - } - - private static SortField sortFieldFrom(MappedFieldType type) { - if (type instanceof KeywordFieldMapper.KeywordFieldType) { - return new SortedSetSortField(type.name(), false); - } else if (type instanceof DateFieldMapper.DateFieldType) { - return new SortedNumericSortField(type.name(), SortField.Type.LONG, false); - } else if (type instanceof NumberFieldMapper.NumberFieldType) { - switch (type.typeName()) { - case "byte": - case "short": - case "integer": - return new SortedNumericSortField(type.name(), SortField.Type.INT, false); - case "long": - return new SortedNumericSortField(type.name(), SortField.Type.LONG, false); - case "float": - case "double": - return new SortedNumericSortField(type.name(), SortField.Type.DOUBLE, false); - default: - return null; - } - } - return null; - } } diff --git a/server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileUtilsTests.java b/server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileUtilsTests.java index dfe4034650594..1443208a1d2fc 100644 --- a/server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileUtilsTests.java +++ b/server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileUtilsTests.java @@ -34,14 +34,15 @@ import org.opensearch.common.geo.GeoPoint; import org.opensearch.geometry.Rectangle; +import org.opensearch.search.aggregations.bucket.GeoTileUtils; import org.opensearch.test.OpenSearchTestCase; -import static org.opensearch.search.aggregations.bucket.geogrid.GeoTileUtils.MAX_ZOOM; -import static org.opensearch.search.aggregations.bucket.geogrid.GeoTileUtils.checkPrecisionRange; -import static org.opensearch.search.aggregations.bucket.geogrid.GeoTileUtils.hashToGeoPoint; -import static org.opensearch.search.aggregations.bucket.geogrid.GeoTileUtils.keyToGeoPoint; -import static org.opensearch.search.aggregations.bucket.geogrid.GeoTileUtils.longEncode; -import static org.opensearch.search.aggregations.bucket.geogrid.GeoTileUtils.stringEncode; +import static org.opensearch.search.aggregations.bucket.GeoTileUtils.MAX_ZOOM; +import static org.opensearch.search.aggregations.bucket.GeoTileUtils.checkPrecisionRange; +import static org.opensearch.search.aggregations.bucket.GeoTileUtils.hashToGeoPoint; +import static org.opensearch.search.aggregations.bucket.GeoTileUtils.keyToGeoPoint; +import static org.opensearch.search.aggregations.bucket.GeoTileUtils.longEncode; +import static org.opensearch.search.aggregations.bucket.GeoTileUtils.stringEncode; import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.closeTo; import static org.hamcrest.Matchers.containsString; diff --git a/test/framework/src/main/java/org/opensearch/search/aggregations/composite/BaseCompositeAggregatorTestCase.java b/test/framework/src/main/java/org/opensearch/search/aggregations/composite/BaseCompositeAggregatorTestCase.java new file mode 100644 index 0000000000000..7d00772913d6e --- /dev/null +++ b/test/framework/src/main/java/org/opensearch/search/aggregations/composite/BaseCompositeAggregatorTestCase.java @@ -0,0 +1,310 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.search.aggregations.composite; + +import org.apache.lucene.document.Document; +import org.apache.lucene.document.DoublePoint; +import org.apache.lucene.document.Field; +import org.apache.lucene.document.InetAddressPoint; +import org.apache.lucene.document.IntPoint; +import org.apache.lucene.document.LongPoint; +import org.apache.lucene.document.SortedNumericDocValuesField; +import org.apache.lucene.document.SortedSetDocValuesField; +import org.apache.lucene.document.StringField; +import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.IndexWriterConfig; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Sort; +import org.apache.lucene.search.SortField; +import org.apache.lucene.search.SortedNumericSortField; +import org.apache.lucene.search.SortedSetSortField; +import org.apache.lucene.store.Directory; +import org.apache.lucene.tests.analysis.MockAnalyzer; +import org.apache.lucene.tests.index.RandomIndexWriter; +import org.apache.lucene.tests.util.TestUtil; +import org.apache.lucene.util.BytesRef; +import org.apache.lucene.util.NumericUtils; +import org.junit.After; +import org.junit.Before; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.text.Text; +import org.opensearch.common.time.DateFormatter; +import org.opensearch.common.time.DateFormatters; +import org.opensearch.index.Index; +import org.opensearch.index.IndexSettings; +import org.opensearch.index.mapper.DateFieldMapper; +import org.opensearch.index.mapper.DocumentMapper; +import org.opensearch.index.mapper.IpFieldMapper; +import org.opensearch.index.mapper.KeywordFieldMapper; +import org.opensearch.index.mapper.MappedFieldType; +import org.opensearch.index.mapper.MapperService; +import org.opensearch.index.mapper.NumberFieldMapper; +import org.opensearch.search.aggregations.AggregatorTestCase; +import org.opensearch.search.aggregations.bucket.composite.CompositeAggregationBuilder; +import org.opensearch.search.aggregations.bucket.composite.CompositeValuesSourceBuilder; +import org.opensearch.search.aggregations.bucket.composite.InternalComposite; +import org.opensearch.test.IndexSettingsModule; + +import java.io.IOException; +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * Base class for the Aggregator Tests which are registered under Composite Aggregation. + */ +public class BaseCompositeAggregatorTestCase extends AggregatorTestCase { + + protected static List FIELD_TYPES; + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + FIELD_TYPES = new ArrayList<>(); + FIELD_TYPES.add(new KeywordFieldMapper.KeywordFieldType("keyword")); + FIELD_TYPES.add(new NumberFieldMapper.NumberFieldType("long", NumberFieldMapper.NumberType.LONG)); + FIELD_TYPES.add(new NumberFieldMapper.NumberFieldType("double", NumberFieldMapper.NumberType.DOUBLE)); + FIELD_TYPES.add(new DateFieldMapper.DateFieldType("date", DateFormatter.forPattern("yyyy-MM-dd||epoch_millis"))); + FIELD_TYPES.add(new NumberFieldMapper.NumberFieldType("price", NumberFieldMapper.NumberType.INTEGER)); + FIELD_TYPES.add(new KeywordFieldMapper.KeywordFieldType("terms")); + FIELD_TYPES.add(new IpFieldMapper.IpFieldType("ip")); + } + + @Override + @After + public void tearDown() throws Exception { + super.tearDown(); + FIELD_TYPES = null; + } + + @Override + protected MapperService mapperServiceMock() { + MapperService mapperService = mock(MapperService.class); + DocumentMapper mapper = mock(DocumentMapper.class); + when(mapper.typeText()).thenReturn(new Text("_doc")); + when(mapper.type()).thenReturn("_doc"); + when(mapperService.documentMapper()).thenReturn(mapper); + return mapperService; + } + + protected static Map> createDocument(Object... fields) { + assert fields.length % 2 == 0; + final Map> map = new HashMap<>(); + for (int i = 0; i < fields.length; i += 2) { + String field = (String) fields[i]; + if (fields[i + 1] instanceof List) { + map.put(field, (List) fields[i + 1]); + } else { + map.put(field, Collections.singletonList(fields[i + 1])); + } + } + return map; + } + + protected void testSearchCase( + List queries, + List>> dataset, + Supplier create, + Consumer verify + ) throws IOException { + for (Query query : queries) { + executeTestCase(false, false, query, dataset, create, verify); + executeTestCase(false, true, query, dataset, create, verify); + } + } + + protected void executeTestCase( + boolean forceMerge, + boolean useIndexSort, + Query query, + List>> dataset, + Supplier create, + Consumer verify + ) throws IOException { + Map types = FIELD_TYPES.stream().collect(Collectors.toMap(MappedFieldType::name, Function.identity())); + CompositeAggregationBuilder aggregationBuilder = create.get(); + Sort indexSort = useIndexSort ? buildIndexSort(aggregationBuilder.sources(), types) : null; + IndexSettings indexSettings = createIndexSettings(indexSort); + try (Directory directory = newDirectory()) { + IndexWriterConfig config = newIndexWriterConfig(random(), new MockAnalyzer(random())); + if (indexSort != null) { + config.setIndexSort(indexSort); + config.setCodec(TestUtil.getDefaultCodec()); + } + try (RandomIndexWriter indexWriter = new RandomIndexWriter(random(), directory, config)) { + Document document = new Document(); + int id = 0; + for (Map> fields : dataset) { + document.clear(); + addToDocument(id, document, fields); + indexWriter.addDocument(document); + id++; + } + if (forceMerge || rarely()) { + // forceMerge randomly or if the collector-per-leaf testing stuff would break the tests. + indexWriter.forceMerge(1); + } else { + if (dataset.size() > 0) { + int numDeletes = randomIntBetween(1, 25); + for (int i = 0; i < numDeletes; i++) { + id = randomIntBetween(0, dataset.size() - 1); + indexWriter.deleteDocuments(new Term("id", Integer.toString(id))); + document.clear(); + addToDocument(id, document, dataset.get(id)); + indexWriter.addDocument(document); + } + } + + } + } + try (IndexReader indexReader = DirectoryReader.open(directory)) { + IndexSearcher indexSearcher = new IndexSearcher(indexReader); + InternalComposite composite = searchAndReduce( + indexSettings, + indexSearcher, + query, + aggregationBuilder, + FIELD_TYPES.toArray(new MappedFieldType[0]) + ); + verify.accept(composite); + } + } + } + + protected void addToDocument(int id, Document doc, Map> keys) { + doc.add(new StringField("id", Integer.toString(id), Field.Store.NO)); + for (Map.Entry> entry : keys.entrySet()) { + final String name = entry.getKey(); + for (Object value : entry.getValue()) { + if (value instanceof Integer) { + doc.add(new SortedNumericDocValuesField(name, (int) value)); + doc.add(new IntPoint(name, (int) value)); + } else if (value instanceof Long) { + doc.add(new SortedNumericDocValuesField(name, (long) value)); + doc.add(new LongPoint(name, (long) value)); + } else if (value instanceof Double) { + doc.add(new SortedNumericDocValuesField(name, NumericUtils.doubleToSortableLong((double) value))); + doc.add(new DoublePoint(name, (double) value)); + } else if (value instanceof String) { + doc.add(new SortedSetDocValuesField(name, new BytesRef((String) value))); + doc.add(new StringField(name, new BytesRef((String) value), Field.Store.NO)); + } else if (value instanceof InetAddress) { + doc.add(new SortedSetDocValuesField(name, new BytesRef(InetAddressPoint.encode((InetAddress) value)))); + doc.add(new InetAddressPoint(name, (InetAddress) value)); + } else { + if (!addValueToDocument(doc, name, value)) throw new AssertionError( + "invalid object: " + value.getClass().getSimpleName() + ); + } + } + } + } + + /** + * Override this function to handle any specific type of value you want to add in the document for doing the + * composite aggregation. If you have added another Composite Aggregation Type then you must override this + * function so that your field value can be added in the document correctly. + * + * @param doc {@link Document} + * @param name {@link String} Field Name + * @param value {@link Object} Field value + * @return boolean true or false, based on if value is added or not + */ + protected boolean addValueToDocument(final Document doc, final String name, final Object value) { + return false; + } + + protected static Sort buildIndexSort(List> sources, Map fieldTypes) { + List sortFields = new ArrayList<>(); + Map remainingFieldTypes = new HashMap<>(fieldTypes); + for (CompositeValuesSourceBuilder source : sources) { + MappedFieldType type = fieldTypes.remove(source.field()); + remainingFieldTypes.remove(source.field()); + SortField sortField = sortFieldFrom(type); + if (sortField == null) { + break; + } + sortFields.add(sortField); + } + while (remainingFieldTypes.size() > 0 && randomBoolean()) { + // Add extra unused sorts + List fields = new ArrayList<>(remainingFieldTypes.keySet()); + Collections.sort(fields); + String field = fields.get(between(0, fields.size() - 1)); + SortField sortField = sortFieldFrom(remainingFieldTypes.remove(field)); + if (sortField != null) { + sortFields.add(sortField); + } + } + return sortFields.size() > 0 ? new Sort(sortFields.toArray(new SortField[0])) : null; + } + + protected static SortField sortFieldFrom(MappedFieldType type) { + if (type instanceof KeywordFieldMapper.KeywordFieldType) { + return new SortedSetSortField(type.name(), false); + } else if (type instanceof DateFieldMapper.DateFieldType) { + return new SortedNumericSortField(type.name(), SortField.Type.LONG, false); + } else if (type instanceof NumberFieldMapper.NumberFieldType) { + switch (type.typeName()) { + case "byte": + case "short": + case "integer": + return new SortedNumericSortField(type.name(), SortField.Type.INT, false); + case "long": + return new SortedNumericSortField(type.name(), SortField.Type.LONG, false); + case "float": + case "double": + return new SortedNumericSortField(type.name(), SortField.Type.DOUBLE, false); + default: + return null; + } + } + return null; + } + + protected static IndexSettings createIndexSettings(Sort sort) { + Settings.Builder builder = Settings.builder(); + if (sort != null) { + String[] fields = Arrays.stream(sort.getSort()).map(SortField::getField).toArray(String[]::new); + String[] orders = Arrays.stream(sort.getSort()).map((o) -> o.getReverse() ? "desc" : "asc").toArray(String[]::new); + builder.putList("index.sort.field", fields); + builder.putList("index.sort.order", orders); + } + return IndexSettingsModule.newIndexSettings(new Index("_index", "0"), builder.build()); + } + + protected static Map createAfterKey(Object... fields) { + assert fields.length % 2 == 0; + final Map map = new HashMap<>(); + for (int i = 0; i < fields.length; i += 2) { + String field = (String) fields[i]; + map.put(field, fields[i + 1]); + } + return map; + } + + protected static long asLong(String dateTime) { + return DateFormatters.from(DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER.parse(dateTime)).toInstant().toEpochMilli(); + } +} diff --git a/test/framework/src/main/java/org/opensearch/test/InternalAggregationTestCase.java b/test/framework/src/main/java/org/opensearch/test/InternalAggregationTestCase.java index a4099d66de28e..5325c48e16913 100644 --- a/test/framework/src/main/java/org/opensearch/test/InternalAggregationTestCase.java +++ b/test/framework/src/main/java/org/opensearch/test/InternalAggregationTestCase.java @@ -68,10 +68,6 @@ import org.opensearch.search.aggregations.bucket.filter.FiltersAggregationBuilder; import org.opensearch.search.aggregations.bucket.filter.ParsedFilter; import org.opensearch.search.aggregations.bucket.filter.ParsedFilters; -import org.opensearch.search.aggregations.bucket.geogrid.GeoHashGridAggregationBuilder; -import org.opensearch.search.aggregations.bucket.geogrid.GeoTileGridAggregationBuilder; -import org.opensearch.search.aggregations.bucket.geogrid.ParsedGeoHashGrid; -import org.opensearch.search.aggregations.bucket.geogrid.ParsedGeoTileGrid; import org.opensearch.search.aggregations.bucket.global.GlobalAggregationBuilder; import org.opensearch.search.aggregations.bucket.global.ParsedGlobal; import org.opensearch.search.aggregations.bucket.histogram.AutoDateHistogramAggregationBuilder; @@ -275,8 +271,6 @@ public ReduceContext forFinalReduction() { map.put(GlobalAggregationBuilder.NAME, (p, c) -> ParsedGlobal.fromXContent(p, (String) c)); map.put(FilterAggregationBuilder.NAME, (p, c) -> ParsedFilter.fromXContent(p, (String) c)); map.put(InternalSampler.PARSER_NAME, (p, c) -> ParsedSampler.fromXContent(p, (String) c)); - map.put(GeoHashGridAggregationBuilder.NAME, (p, c) -> ParsedGeoHashGrid.fromXContent(p, (String) c)); - map.put(GeoTileGridAggregationBuilder.NAME, (p, c) -> ParsedGeoTileGrid.fromXContent(p, (String) c)); map.put(RangeAggregationBuilder.NAME, (p, c) -> ParsedRange.fromXContent(p, (String) c)); map.put(DateRangeAggregationBuilder.NAME, (p, c) -> ParsedDateRange.fromXContent(p, (String) c)); map.put(GeoDistanceAggregationBuilder.NAME, (p, c) -> ParsedGeoDistance.fromXContent(p, (String) c)); From 9d6255c61748ffdf0afde11a3724d00d4b1f1d05 Mon Sep 17 00:00:00 2001 From: Bharathwaj G <58062316+bharath-techie@users.noreply.github.com> Date: Tue, 23 Aug 2022 18:12:36 +0530 Subject: [PATCH 005/187] Add changes to Point in time segments API service layer (#4105) * pit segments service layer changes Signed-off-by: Bharathwaj G * Addressing comment Signed-off-by: Bharathwaj G * Addressing comment Signed-off-by: Bharathwaj G * Addressing comment Signed-off-by: Bharathwaj G * addressing review comments Signed-off-by: Bharathwaj G * addressing comment Signed-off-by: Bharathwaj G * Addressing comments Signed-off-by: Bharathwaj G * addressing comments Signed-off-by: Bharathwaj G * Addressing comments Signed-off-by: Bharathwaj G * Adding '_all' as option to get all segments Signed-off-by: Bharathwaj G Signed-off-by: Bharathwaj G --- .../org/opensearch/action/ActionModule.java | 3 + .../indices/segments/PitSegmentsAction.java | 24 ++ .../indices/segments/PitSegmentsRequest.java | 87 ++++++ .../segments/TransportPitSegmentsAction.java | 261 ++++++++++++++++++ .../action/search/CreatePitResponse.java | 2 +- .../node/TransportBroadcastByNodeAction.java | 9 +- .../java/org/opensearch/client/Client.java | 7 + .../client/support/AbstractClient.java | 7 + .../cluster/routing/ShardRouting.java | 4 +- .../search/internal/PitReaderContext.java | 24 ++ .../action/search/PitTestsUtil.java | 23 +- .../search/CreatePitSingleNodeTests.java | 16 +- .../opensearch/search/PitMultiNodeTests.java | 3 + 13 files changed, 464 insertions(+), 6 deletions(-) create mode 100644 server/src/main/java/org/opensearch/action/admin/indices/segments/PitSegmentsAction.java create mode 100644 server/src/main/java/org/opensearch/action/admin/indices/segments/PitSegmentsRequest.java create mode 100644 server/src/main/java/org/opensearch/action/admin/indices/segments/TransportPitSegmentsAction.java diff --git a/server/src/main/java/org/opensearch/action/ActionModule.java b/server/src/main/java/org/opensearch/action/ActionModule.java index 052d2ec2b5764..797c5c38fada6 100644 --- a/server/src/main/java/org/opensearch/action/ActionModule.java +++ b/server/src/main/java/org/opensearch/action/ActionModule.java @@ -165,7 +165,9 @@ import org.opensearch.action.admin.indices.rollover.RolloverAction; import org.opensearch.action.admin.indices.rollover.TransportRolloverAction; import org.opensearch.action.admin.indices.segments.IndicesSegmentsAction; +import org.opensearch.action.admin.indices.segments.PitSegmentsAction; import org.opensearch.action.admin.indices.segments.TransportIndicesSegmentsAction; +import org.opensearch.action.admin.indices.segments.TransportPitSegmentsAction; import org.opensearch.action.admin.indices.settings.get.GetSettingsAction; import org.opensearch.action.admin.indices.settings.get.TransportGetSettingsAction; import org.opensearch.action.admin.indices.settings.put.TransportUpdateSettingsAction; @@ -671,6 +673,7 @@ public void reg actions.register(CreatePitAction.INSTANCE, TransportCreatePitAction.class); actions.register(GetAllPitsAction.INSTANCE, TransportGetAllPitsAction.class); actions.register(DeletePitAction.INSTANCE, TransportDeletePitAction.class); + actions.register(PitSegmentsAction.INSTANCE, TransportPitSegmentsAction.class); // Remote Store actions.register(RestoreRemoteStoreAction.INSTANCE, TransportRestoreRemoteStoreAction.class); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/segments/PitSegmentsAction.java b/server/src/main/java/org/opensearch/action/admin/indices/segments/PitSegmentsAction.java new file mode 100644 index 0000000000000..b52ef32a91b16 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/segments/PitSegmentsAction.java @@ -0,0 +1,24 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.indices.segments; + +import org.opensearch.action.ActionType; + +/** + * Action for retrieving segment information for PITs + */ +public class PitSegmentsAction extends ActionType { + + public static final PitSegmentsAction INSTANCE = new PitSegmentsAction(); + public static final String NAME = "indices:monitor/point_in_time/segments"; + + private PitSegmentsAction() { + super(NAME, IndicesSegmentResponse::new); + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/indices/segments/PitSegmentsRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/segments/PitSegmentsRequest.java new file mode 100644 index 0000000000000..84f5e5ad6a1e8 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/segments/PitSegmentsRequest.java @@ -0,0 +1,87 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.indices.segments; + +import org.opensearch.action.ActionRequestValidationException; +import org.opensearch.action.support.broadcast.BroadcastRequest; +import org.opensearch.common.Strings; +import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.common.io.stream.StreamOutput; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.opensearch.action.ValidateActions.addValidationError; + +/** + * Transport request for retrieving PITs segment information + */ +public class PitSegmentsRequest extends BroadcastRequest { + private boolean verbose = false; + private final List pitIds = new ArrayList<>(); + + public PitSegmentsRequest() { + this(Strings.EMPTY_ARRAY); + } + + public PitSegmentsRequest(StreamInput in) throws IOException { + super(in); + pitIds.addAll(Arrays.asList(in.readStringArray())); + verbose = in.readBoolean(); + } + + public PitSegmentsRequest(String... pitIds) { + super(pitIds); + this.pitIds.addAll(Arrays.asList(pitIds)); + } + + /** + * true if detailed information about each segment should be returned, + * false otherwise. + */ + public boolean isVerbose() { + return verbose; + } + + /** + * Sets the verbose option. + * @see #isVerbose() + */ + public void setVerbose(boolean v) { + verbose = v; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + out.writeStringArrayNullable((pitIds == null) ? null : pitIds.toArray(new String[pitIds.size()])); + out.writeBoolean(verbose); + } + + public List getPitIds() { + return Collections.unmodifiableList(pitIds); + } + + public void clearAndSetPitIds(List pitIds) { + this.pitIds.clear(); + this.pitIds.addAll(pitIds); + } + + @Override + public ActionRequestValidationException validate() { + ActionRequestValidationException validationException = null; + if (pitIds == null || pitIds.isEmpty()) { + validationException = addValidationError("no pit ids specified", validationException); + } + return validationException; + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/indices/segments/TransportPitSegmentsAction.java b/server/src/main/java/org/opensearch/action/admin/indices/segments/TransportPitSegmentsAction.java new file mode 100644 index 0000000000000..9d4ece74a7270 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/segments/TransportPitSegmentsAction.java @@ -0,0 +1,261 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ +package org.opensearch.action.admin.indices.segments; + +import org.opensearch.action.ActionListener; +import org.opensearch.action.search.ListPitInfo; +import org.opensearch.action.search.PitService; +import org.opensearch.action.search.SearchContextId; +import org.opensearch.action.search.SearchContextIdForNode; +import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.DefaultShardOperationFailedException; +import org.opensearch.action.support.broadcast.node.TransportBroadcastByNodeAction; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.block.ClusterBlockException; +import org.opensearch.cluster.block.ClusterBlockLevel; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.routing.AllocationId; +import org.opensearch.cluster.routing.PlainShardsIterator; +import org.opensearch.cluster.routing.RecoverySource; +import org.opensearch.cluster.routing.ShardRouting; +import org.opensearch.cluster.routing.ShardRoutingState; +import org.opensearch.cluster.routing.ShardsIterator; +import org.opensearch.cluster.routing.UnassignedInfo; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.Strings; +import org.opensearch.common.inject.Inject; +import org.opensearch.common.io.stream.NamedWriteableRegistry; +import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.index.shard.ShardId; +import org.opensearch.indices.IndicesService; +import org.opensearch.search.SearchService; +import org.opensearch.search.internal.PitReaderContext; +import org.opensearch.tasks.Task; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportService; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static org.opensearch.action.search.SearchContextId.decode; + +/** + * Transport action for retrieving segment information of PITs + */ +public class TransportPitSegmentsAction extends TransportBroadcastByNodeAction { + private final ClusterService clusterService; + private final IndicesService indicesService; + private final SearchService searchService; + private final NamedWriteableRegistry namedWriteableRegistry; + private final TransportService transportService; + private final PitService pitService; + + @Inject + public TransportPitSegmentsAction( + ClusterService clusterService, + TransportService transportService, + IndicesService indicesService, + ActionFilters actionFilters, + IndexNameExpressionResolver indexNameExpressionResolver, + SearchService searchService, + NamedWriteableRegistry namedWriteableRegistry, + PitService pitService + ) { + super( + PitSegmentsAction.NAME, + clusterService, + transportService, + actionFilters, + indexNameExpressionResolver, + PitSegmentsRequest::new, + ThreadPool.Names.MANAGEMENT + ); + this.clusterService = clusterService; + this.indicesService = indicesService; + this.searchService = searchService; + this.namedWriteableRegistry = namedWriteableRegistry; + this.transportService = transportService; + this.pitService = pitService; + } + + /** + * Execute PIT segments flow for all PITs or request PIT IDs + */ + @Override + protected void doExecute(Task task, PitSegmentsRequest request, ActionListener listener) { + List pitIds = request.getPitIds(); + if (pitIds.size() == 1 && "_all".equals(pitIds.get(0))) { + pitService.getAllPits(ActionListener.wrap(response -> { + request.clearAndSetPitIds(response.getPitInfos().stream().map(ListPitInfo::getPitId).collect(Collectors.toList())); + super.doExecute(task, request, listener); + }, listener::onFailure)); + } else { + super.doExecute(task, request, listener); + } + } + + /** + * This adds list of shards on which we need to retrieve pit segments details + * @param clusterState the cluster state + * @param request the underlying request + * @param concreteIndices the concrete indices on which to execute the operation + */ + @Override + protected ShardsIterator shards(ClusterState clusterState, PitSegmentsRequest request, String[] concreteIndices) { + final ArrayList iterators = new ArrayList<>(); + for (String pitId : request.getPitIds()) { + SearchContextId searchContext = decode(namedWriteableRegistry, pitId); + for (Map.Entry entry : searchContext.shards().entrySet()) { + final SearchContextIdForNode perNode = entry.getValue(); + // check if node is part of local cluster + if (Strings.isEmpty(perNode.getClusterAlias())) { + final ShardId shardId = entry.getKey(); + iterators.add( + new PitAwareShardRouting( + pitId, + shardId, + perNode.getNode(), + null, + true, + ShardRoutingState.STARTED, + null, + null, + null, + -1L + ) + ); + } + } + } + return new PlainShardsIterator(iterators); + } + + @Override + protected ClusterBlockException checkGlobalBlock(ClusterState state, PitSegmentsRequest request) { + return state.blocks().globalBlockedException(ClusterBlockLevel.METADATA_READ); + } + + @Override + protected ClusterBlockException checkRequestBlock(ClusterState state, PitSegmentsRequest countRequest, String[] concreteIndices) { + return state.blocks().indicesBlockedException(ClusterBlockLevel.METADATA_READ, concreteIndices); + } + + @Override + protected ShardSegments readShardResult(StreamInput in) throws IOException { + return new ShardSegments(in); + } + + @Override + protected IndicesSegmentResponse newResponse( + PitSegmentsRequest request, + int totalShards, + int successfulShards, + int failedShards, + List results, + List shardFailures, + ClusterState clusterState + ) { + return new IndicesSegmentResponse( + results.toArray(new ShardSegments[results.size()]), + totalShards, + successfulShards, + failedShards, + shardFailures + ); + } + + @Override + protected PitSegmentsRequest readRequestFrom(StreamInput in) throws IOException { + return new PitSegmentsRequest(in); + } + + @Override + public List getShardRoutingsFromInputStream(StreamInput in) throws IOException { + return in.readList(PitAwareShardRouting::new); + } + + /** + * This retrieves segment details of PIT context + * @param request the node-level request + * @param shardRouting the shard on which to execute the operation + */ + @Override + protected ShardSegments shardOperation(PitSegmentsRequest request, ShardRouting shardRouting) { + assert shardRouting instanceof PitAwareShardRouting; + PitAwareShardRouting pitAwareShardRouting = (PitAwareShardRouting) shardRouting; + SearchContextIdForNode searchContextIdForNode = decode(namedWriteableRegistry, pitAwareShardRouting.getPitId()).shards() + .get(shardRouting.shardId()); + PitReaderContext pitReaderContext = searchService.getPitReaderContext(searchContextIdForNode.getSearchContextId()); + if (pitReaderContext == null) { + return new ShardSegments(shardRouting, Collections.emptyList()); + } + return new ShardSegments(pitReaderContext.getShardRouting(), pitReaderContext.getSegments()); + } + + /** + * This holds PIT id which is used to perform broadcast operation in PIT shards to retrieve segments information + */ + public class PitAwareShardRouting extends ShardRouting { + + private final String pitId; + + public PitAwareShardRouting(StreamInput in) throws IOException { + super(in); + this.pitId = in.readString(); + } + + public PitAwareShardRouting( + String pitId, + ShardId shardId, + String currentNodeId, + String relocatingNodeId, + boolean primary, + ShardRoutingState state, + RecoverySource recoverySource, + UnassignedInfo unassignedInfo, + AllocationId allocationId, + long expectedShardSize + ) { + super( + shardId, + currentNodeId, + relocatingNodeId, + primary, + state, + recoverySource, + unassignedInfo, + allocationId, + expectedShardSize + ); + this.pitId = pitId; + } + + public String getPitId() { + return pitId; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + out.writeString(pitId); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + super.toXContent(builder, params); + builder.field("pit_id", pitId); + return builder.endObject(); + } + } +} diff --git a/server/src/main/java/org/opensearch/action/search/CreatePitResponse.java b/server/src/main/java/org/opensearch/action/search/CreatePitResponse.java index 25eb9aff9e3d7..dd197a37f8616 100644 --- a/server/src/main/java/org/opensearch/action/search/CreatePitResponse.java +++ b/server/src/main/java/org/opensearch/action/search/CreatePitResponse.java @@ -28,7 +28,7 @@ * Create point in time response with point in time id and shard success / failures */ public class CreatePitResponse extends ActionResponse implements StatusToXContentObject { - private static final ParseField ID = new ParseField("id"); + private static final ParseField ID = new ParseField("pit_id"); private static final ParseField CREATION_TIME = new ParseField("creation_time"); // point in time id diff --git a/server/src/main/java/org/opensearch/action/support/broadcast/node/TransportBroadcastByNodeAction.java b/server/src/main/java/org/opensearch/action/support/broadcast/node/TransportBroadcastByNodeAction.java index f849be4db4e2b..9e353a35831d0 100644 --- a/server/src/main/java/org/opensearch/action/support/broadcast/node/TransportBroadcastByNodeAction.java +++ b/server/src/main/java/org/opensearch/action/support/broadcast/node/TransportBroadcastByNodeAction.java @@ -532,6 +532,13 @@ private void onShardOperation( } } + /** + * This method reads ShardRouting from input stream + */ + public List getShardRoutingsFromInputStream(StreamInput in) throws IOException { + return in.readList(ShardRouting::new); + } + /** * A node request * @@ -547,7 +554,7 @@ public class NodeRequest extends TransportRequest implements IndicesRequest { public NodeRequest(StreamInput in) throws IOException { super(in); indicesLevelRequest = readRequestFrom(in); - shards = in.readList(ShardRouting::new); + shards = getShardRoutingsFromInputStream(in); nodeId = in.readString(); } diff --git a/server/src/main/java/org/opensearch/client/Client.java b/server/src/main/java/org/opensearch/client/Client.java index 1d3bbfcba43f9..94043d5c3c89f 100644 --- a/server/src/main/java/org/opensearch/client/Client.java +++ b/server/src/main/java/org/opensearch/client/Client.java @@ -34,6 +34,8 @@ import org.opensearch.action.ActionFuture; import org.opensearch.action.ActionListener; +import org.opensearch.action.admin.indices.segments.IndicesSegmentResponse; +import org.opensearch.action.admin.indices.segments.PitSegmentsRequest; import org.opensearch.action.bulk.BulkRequest; import org.opensearch.action.bulk.BulkRequestBuilder; import org.opensearch.action.bulk.BulkResponse; @@ -339,6 +341,11 @@ public interface Client extends OpenSearchClient, Releasable { */ void deletePits(DeletePitRequest deletePITRequest, ActionListener listener); + /** + * Get information of segments of one or more PITs + */ + void pitSegments(PitSegmentsRequest pitSegmentsRequest, ActionListener listener); + /** * Performs multiple search requests. */ diff --git a/server/src/main/java/org/opensearch/client/support/AbstractClient.java b/server/src/main/java/org/opensearch/client/support/AbstractClient.java index 7084a856ab3d1..bc80a2ba92bf8 100644 --- a/server/src/main/java/org/opensearch/client/support/AbstractClient.java +++ b/server/src/main/java/org/opensearch/client/support/AbstractClient.java @@ -240,6 +240,8 @@ import org.opensearch.action.admin.indices.segments.IndicesSegmentsAction; import org.opensearch.action.admin.indices.segments.IndicesSegmentsRequest; import org.opensearch.action.admin.indices.segments.IndicesSegmentsRequestBuilder; +import org.opensearch.action.admin.indices.segments.PitSegmentsAction; +import org.opensearch.action.admin.indices.segments.PitSegmentsRequest; import org.opensearch.action.admin.indices.settings.get.GetSettingsAction; import org.opensearch.action.admin.indices.settings.get.GetSettingsRequest; import org.opensearch.action.admin.indices.settings.get.GetSettingsRequestBuilder; @@ -593,6 +595,11 @@ public void deletePits(final DeletePitRequest deletePITRequest, final ActionList execute(DeletePitAction.INSTANCE, deletePITRequest, listener); } + @Override + public void pitSegments(final PitSegmentsRequest request, final ActionListener listener) { + execute(PitSegmentsAction.INSTANCE, request, listener); + } + @Override public ActionFuture multiSearch(MultiSearchRequest request) { return execute(MultiSearchAction.INSTANCE, request); diff --git a/server/src/main/java/org/opensearch/cluster/routing/ShardRouting.java b/server/src/main/java/org/opensearch/cluster/routing/ShardRouting.java index 7dec8f9c84a89..e3aa2a666d454 100644 --- a/server/src/main/java/org/opensearch/cluster/routing/ShardRouting.java +++ b/server/src/main/java/org/opensearch/cluster/routing/ShardRouting.java @@ -54,7 +54,7 @@ * * @opensearch.internal */ -public final class ShardRouting implements Writeable, ToXContentObject { +public class ShardRouting implements Writeable, ToXContentObject { /** * Used if shard size is not available @@ -78,7 +78,7 @@ public final class ShardRouting implements Writeable, ToXContentObject { * A constructor to internally create shard routing instances, note, the internal flag should only be set to true * by either this class or tests. Visible for testing. */ - ShardRouting( + protected ShardRouting( ShardId shardId, String currentNodeId, String relocatingNodeId, diff --git a/server/src/main/java/org/opensearch/search/internal/PitReaderContext.java b/server/src/main/java/org/opensearch/search/internal/PitReaderContext.java index 98e84136a8847..b24a8a4172e29 100644 --- a/server/src/main/java/org/opensearch/search/internal/PitReaderContext.java +++ b/server/src/main/java/org/opensearch/search/internal/PitReaderContext.java @@ -9,12 +9,17 @@ package org.opensearch.search.internal; import org.apache.lucene.util.SetOnce; +import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.common.lease.Releasable; import org.opensearch.common.lease.Releasables; import org.opensearch.index.IndexService; import org.opensearch.index.engine.Engine; +import org.opensearch.index.engine.Segment; import org.opensearch.index.shard.IndexShard; +import java.util.Collections; +import java.util.List; + /** * PIT reader context containing PIT specific information such as pit id, create time etc. */ @@ -24,6 +29,15 @@ public class PitReaderContext extends ReaderContext { private final SetOnce pitId = new SetOnce<>(); // Creation time of PIT contexts which helps users to differentiate between multiple PIT reader contexts private final SetOnce creationTime = new SetOnce<>(); + /** + * Shard routing at the time of creation of PIT Reader Context + */ + private final ShardRouting shardRouting; + + /** + * Encapsulates segments constituting the shard at the time of creation of PIT Reader Context. + */ + private final List segments; public PitReaderContext( ShardSearchContextId id, @@ -34,6 +48,8 @@ public PitReaderContext( boolean singleSession ) { super(id, indexService, indexShard, searcherSupplier, keepAliveInMillis, singleSession); + shardRouting = indexShard.routingEntry(); + segments = indexShard.segments(true); } public String getPitId() { @@ -67,4 +83,12 @@ public long getCreationTime() { public void setCreationTime(final long creationTime) { this.creationTime.set(creationTime); } + + public ShardRouting getShardRouting() { + return shardRouting; + } + + public List getSegments() { + return Collections.unmodifiableList(segments); + } } diff --git a/server/src/test/java/org/opensearch/action/search/PitTestsUtil.java b/server/src/test/java/org/opensearch/action/search/PitTestsUtil.java index 433cd9dfa3e89..60a31c62dc32d 100644 --- a/server/src/test/java/org/opensearch/action/search/PitTestsUtil.java +++ b/server/src/test/java/org/opensearch/action/search/PitTestsUtil.java @@ -14,6 +14,9 @@ import org.opensearch.action.ActionFuture; import org.opensearch.action.admin.cluster.state.ClusterStateRequest; import org.opensearch.action.admin.cluster.state.ClusterStateResponse; +import org.opensearch.action.admin.indices.segments.IndicesSegmentResponse; +import org.opensearch.action.admin.indices.segments.PitSegmentsAction; +import org.opensearch.action.admin.indices.segments.PitSegmentsRequest; import org.opensearch.client.Client; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.common.util.concurrent.AtomicArray; @@ -33,6 +36,8 @@ import java.util.Map; import java.util.concurrent.ExecutionException; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import static org.opensearch.test.OpenSearchTestCase.between; import static org.opensearch.test.OpenSearchTestCase.randomAlphaOfLength; import static org.opensearch.test.OpenSearchTestCase.randomBoolean; @@ -107,7 +112,7 @@ public static void assertUsingGetAllPits(Client client, String id, long creation GetAllPitNodesRequest getAllPITNodesRequest = new GetAllPitNodesRequest(disNodesArr); ActionFuture execute1 = client.execute(GetAllPitsAction.INSTANCE, getAllPITNodesRequest); GetAllPitNodesResponse getPitResponse = execute1.get(); - Assert.assertTrue(getPitResponse.getPitInfos().get(0).getPitId().contains(id)); + assertTrue(getPitResponse.getPitInfos().get(0).getPitId().contains(id)); Assert.assertEquals(getPitResponse.getPitInfos().get(0).getCreationTime(), creationTime); } @@ -128,4 +133,20 @@ public static void assertGetAllPitsEmpty(Client client) throws ExecutionExceptio GetAllPitNodesResponse getPitResponse = execute1.get(); Assert.assertEquals(0, getPitResponse.getPitInfos().size()); } + + public static void assertSegments(boolean isEmpty, String index, long expectedShardSize, Client client) { + PitSegmentsRequest pitSegmentsRequest = new PitSegmentsRequest("_all"); + IndicesSegmentResponse indicesSegmentResponse = client.execute(PitSegmentsAction.INSTANCE, pitSegmentsRequest).actionGet(); + assertTrue(indicesSegmentResponse.getShardFailures() == null || indicesSegmentResponse.getShardFailures().length == 0); + assertEquals(indicesSegmentResponse.getIndices().isEmpty(), isEmpty); + if (!isEmpty) { + assertTrue(indicesSegmentResponse.getIndices().get(index) != null); + assertTrue(indicesSegmentResponse.getIndices().get(index).getIndex().equalsIgnoreCase(index)); + assertEquals(expectedShardSize, indicesSegmentResponse.getIndices().get(index).getShards().size()); + } + } + + public static void assertSegments(boolean isEmpty, Client client) { + assertSegments(isEmpty, "index", 2, client); + } } diff --git a/server/src/test/java/org/opensearch/search/CreatePitSingleNodeTests.java b/server/src/test/java/org/opensearch/search/CreatePitSingleNodeTests.java index b730dc01c4871..a10f004b2ee97 100644 --- a/server/src/test/java/org/opensearch/search/CreatePitSingleNodeTests.java +++ b/server/src/test/java/org/opensearch/search/CreatePitSingleNodeTests.java @@ -33,6 +33,7 @@ import java.util.concurrent.ExecutionException; import static org.hamcrest.CoreMatchers.equalTo; +import static org.opensearch.action.search.PitTestsUtil.assertSegments; import static org.opensearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.index.query.QueryBuilders.matchAllQuery; @@ -68,6 +69,7 @@ public void testCreatePITSuccess() throws ExecutionException, InterruptedExcepti ActionFuture execute = client().execute(CreatePitAction.INSTANCE, request); CreatePitResponse pitResponse = execute.get(); PitTestsUtil.assertUsingGetAllPits(client(), pitResponse.getId(), pitResponse.getCreationTime()); + assertSegments(false, client()); client().prepareIndex("index").setId("2").setSource("field", "value").setRefreshPolicy(IMMEDIATE).get(); SearchResponse searchResponse = client().prepareSearch("index") .setSize(2) @@ -80,6 +82,7 @@ public void testCreatePITSuccess() throws ExecutionException, InterruptedExcepti validatePitStats("index", 1, 0, 0); validatePitStats("index", 1, 0, 1); service.doClose(); // this kills the keep-alive reaper we have to reset the node after this test + assertSegments(true, client()); validatePitStats("index", 0, 1, 0); validatePitStats("index", 0, 1, 1); } @@ -96,12 +99,14 @@ public void testCreatePITWithMultipleIndicesSuccess() throws ExecutionException, ActionFuture execute = client().execute(CreatePitAction.INSTANCE, request); CreatePitResponse response = execute.get(); PitTestsUtil.assertUsingGetAllPits(client(), response.getId(), response.getCreationTime()); + assertSegments(false, client()); assertEquals(4, response.getSuccessfulShards()); assertEquals(4, service.getActiveContexts()); validatePitStats("index", 1, 0, 0); validatePitStats("index1", 1, 0, 0); service.doClose(); + assertSegments(true, client()); validatePitStats("index", 0, 1, 0); validatePitStats("index1", 0, 1, 0); } @@ -115,6 +120,7 @@ public void testCreatePITWithShardReplicasSuccess() throws ExecutionException, I ActionFuture execute = client().execute(CreatePitAction.INSTANCE, request); CreatePitResponse pitResponse = execute.get(); PitTestsUtil.assertUsingGetAllPits(client(), pitResponse.getId(), pitResponse.getCreationTime()); + assertSegments(false, client()); client().prepareIndex("index").setId("2").setSource("field", "value").setRefreshPolicy(IMMEDIATE).get(); SearchResponse searchResponse = client().prepareSearch("index") .setSize(2) @@ -127,6 +133,7 @@ public void testCreatePITWithShardReplicasSuccess() throws ExecutionException, I validatePitStats("index", 1, 0, 0); validatePitStats("index", 1, 0, 1); service.doClose(); + assertSegments(true, client()); validatePitStats("index", 0, 1, 0); validatePitStats("index", 0, 1, 1); } @@ -144,6 +151,7 @@ public void testCreatePITWithNonExistentIndex() { assertTrue(ex.getMessage().contains("no such index [index1]")); assertEquals(0, service.getActiveContexts()); + assertSegments(true, client()); service.doClose(); } @@ -164,6 +172,7 @@ public void testCreatePITOnCloseIndex() throws ExecutionException, InterruptedEx SearchService service = getInstanceFromNode(SearchService.class); assertEquals(0, service.getActiveContexts()); PitTestsUtil.assertGetAllPitsEmpty(client()); + assertSegments(true, client()); service.doClose(); } @@ -187,6 +196,7 @@ public void testPitSearchOnDeletedIndex() throws ExecutionException, Interrupted SearchService service = getInstanceFromNode(SearchService.class); PitTestsUtil.assertGetAllPitsEmpty(client()); assertEquals(0, service.getActiveContexts()); + assertSegments(true, client()); service.doClose(); } @@ -212,6 +222,7 @@ public void testPitSearchOnCloseIndex() throws ExecutionException, InterruptedEx ActionFuture execute = client().execute(CreatePitAction.INSTANCE, request); CreatePitResponse pitResponse = execute.get(); PitTestsUtil.assertUsingGetAllPits(client(), pitResponse.getId(), pitResponse.getCreationTime()); + assertSegments(false, client()); SearchService service = getInstanceFromNode(SearchService.class); assertEquals(2, service.getActiveContexts()); validatePitStats("index", 1, 0, 0); @@ -227,7 +238,7 @@ public void testPitSearchOnCloseIndex() throws ExecutionException, InterruptedEx assertTrue(ex.shardFailures()[0].reason().contains("SearchContextMissingException")); assertEquals(0, service.getActiveContexts()); PitTestsUtil.assertGetAllPitsEmpty(client()); - + assertSegments(true, client()); // PIT reader contexts are lost after close, verifying it with open index api client().admin().indices().prepareOpen("index").get(); ex = expectThrows(SearchPhaseExecutionException.class, () -> { @@ -491,6 +502,7 @@ public void testPitAfterUpdateIndex() throws Exception { assertEquals(0, service.getActiveContexts()); validatePitStats("test", 0, 1, 0); PitTestsUtil.assertGetAllPitsEmpty(client()); + assertSegments(true, client()); } } @@ -503,6 +515,7 @@ public void testConcurrentSearches() throws Exception { ActionFuture execute = client().execute(CreatePitAction.INSTANCE, request); CreatePitResponse pitResponse = execute.get(); PitTestsUtil.assertUsingGetAllPits(client(), pitResponse.getId(), pitResponse.getCreationTime()); + assertSegments(false, client()); Thread[] threads = new Thread[5]; CountDownLatch latch = new CountDownLatch(threads.length); @@ -538,6 +551,7 @@ public void testConcurrentSearches() throws Exception { validatePitStats("index", 0, 1, 0); validatePitStats("index", 0, 1, 1); PitTestsUtil.assertGetAllPitsEmpty(client()); + assertSegments(true, client()); } public void validatePitStats(String index, long expectedPitCurrent, long expectedPitCount, int shardId) throws ExecutionException, diff --git a/server/src/test/java/org/opensearch/search/PitMultiNodeTests.java b/server/src/test/java/org/opensearch/search/PitMultiNodeTests.java index 29126d786770e..d29ccf5b97138 100644 --- a/server/src/test/java/org/opensearch/search/PitMultiNodeTests.java +++ b/server/src/test/java/org/opensearch/search/PitMultiNodeTests.java @@ -51,6 +51,7 @@ import java.util.stream.Collectors; import static org.hamcrest.Matchers.containsString; +import static org.opensearch.action.search.PitTestsUtil.assertSegments; import static org.opensearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; @@ -85,6 +86,7 @@ public void testPit() throws Exception { assertEquals(2, searchResponse.getTotalShards()); validatePitStats("index", 2, 2); PitTestsUtil.assertUsingGetAllPits(client(), pitResponse.getId(), pitResponse.getCreationTime()); + assertSegments(false, client()); } public void testCreatePitWhileNodeDropWithAllowPartialCreationFalse() throws Exception { @@ -112,6 +114,7 @@ public Settings onNodeStopped(String nodeName) throws Exception { ActionFuture execute = client().execute(CreatePitAction.INSTANCE, request); CreatePitResponse pitResponse = execute.get(); PitTestsUtil.assertUsingGetAllPits(client(), pitResponse.getId(), pitResponse.getCreationTime()); + assertSegments(false, "index", 1, client()); assertEquals(1, pitResponse.getSuccessfulShards()); assertEquals(2, pitResponse.getTotalShards()); SearchResponse searchResponse = client().prepareSearch("index") From 53277674107e90057b99afa316f0ed4166f5a3c7 Mon Sep 17 00:00:00 2001 From: Kunal Kotwani Date: Tue, 23 Aug 2022 10:18:40 -0700 Subject: [PATCH 006/187] Add workflow for changelog verification (#4085) * Add workflow for changelog verification Signed-off-by: Kunal Kotwani * Update format for changelog, add developer documentation Signed-off-by: Kunal Kotwani * Update link reference to be relative to project Signed-off-by: Kunal Kotwani * Fix links for CHANGELOG versions Signed-off-by: Kunal Kotwani * Update contribution guide Signed-off-by: Kunal Kotwani Signed-off-by: Kunal Kotwani --- .github/pull_request_template.md | 7 ++++--- .github/workflows/changelog_verifier.yml | 22 ++++++++++++++++++++++ CHANGELOG.md | 19 +++++++++++++++++++ CONTRIBUTING.md | 20 ++++++++++++++++++++ 4 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/changelog_verifier.yml create mode 100644 CHANGELOG.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index d7981b5113972..c76e27d6dfc7d 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,15 +1,16 @@ ### Description [Describe what this change achieves] - + ### Issues Resolved [List any issues this PR will resolve] - + ### Check List - [ ] New functionality includes testing. - [ ] All tests pass - [ ] New functionality has been documented. - [ ] New functionality has javadoc added -- [ ] Commits are signed per the DCO using --signoff +- [ ] Commits are signed per the DCO using --signoff +- [ ] Commit changes are listed out in CHANGELOG.md file (See: [Changelog](../CONTRIBUTING.md#changelog)) By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. For more information on following Developer Certificate of Origin and signing off your commits, please check [here](https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin). diff --git a/.github/workflows/changelog_verifier.yml b/.github/workflows/changelog_verifier.yml new file mode 100644 index 0000000000000..505b02426f22c --- /dev/null +++ b/.github/workflows/changelog_verifier.yml @@ -0,0 +1,22 @@ +name: "Changelog Verifier" +on: + pull_request: + types: [opened, edited, review_requested, synchronize, reopened, ready_for_review, labeled, unlabeled] + +jobs: + # Enforces the update of a changelog file on every pull request + verify-changelog: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + token: ${{ secrets.GITHUB_TOKEN }} + ref: ${{ github.event.pull_request.head.ref }} + + - uses: dangoslen/dependabot-changelog-helper@v1 + + - uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: "Update changelog" + + - uses: dangoslen/changelog-enforcer@v3 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000000..2a02bfdaf0320 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,19 @@ +# CHANGELOG +Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) + +## [Unreleased] +### Added +- Github workflow for changelog verification ([#4085](https://github.com/opensearch-project/OpenSearch/pull/4085)) + +### Changed + +### Deprecated + +### Removed + +### Fixed + +### Security + + +[Unreleased]: https://github.com/opensearch-project/OpenSearch/compare/2.2.0...HEAD diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 467c7716cc578..16821b1915032 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,6 +6,7 @@ - [Documentation Changes](#documentation-changes) - [Contributing Code](#contributing-code) - [Developer Certificate of Origin](#developer-certificate-of-origin) + - [Changelog](#changelog) - [Review Process](#review-process) # Contributing to OpenSearch @@ -116,6 +117,25 @@ Signed-off-by: Jane Smith ``` You may type this line on your own when writing your commit messages. However, if your user.name and user.email are set in your git configs, you can use `-s` or `--signoff` to add the `Signed-off-by` line to the end of the commit message. +## Changelog + +OpenSearch maintains version specific changelog by enforcing a change to the ongoing [CHANGELOG](CHANGELOG.md) file adhering to the [Keep A Changelog](https://keepachangelog.com/en/1.0.0/) format. + +Briefly, the changes are curated by version, with the changes to the main branch added chronologically to `Unreleased` version. Further, each version has corresponding sections which list out the category of the change - `Added`, `Changed`, `Deprecated`, `Removed`, `Fixed`, `Security`. + + +### How to add my changes to [CHANGELOG](CHANGELOG.md)? + +As a contributor, you must ensure that every pull request has the changes listed out within the corresponding version and appropriate section of [CHANGELOG](CHANGELOG.md) file. + +Adding in the change is two step process - +1. Add your changes to the corresponding section within the CHANGELOG file with dummy pull request information, publish the PR + + `Your change here ([#PR_NUMBER](PR_URL))` + +2. Update the entry for your change in [`CHANGELOG.md`](CHANGELOG.md) and make sure that you reference the pull request there. + + ## Review Process We deeply appreciate everyone who takes the time to make a contribution. We will review all contributions as quickly as possible. As a reminder, [opening an issue](https://github.com/opensearch-project/OpenSearch/issues/new/choose) discussing your change before you make it is the best way to smooth the PR process. This will prevent a rejection because someone else is already working on the problem, or because the solution is incompatible with the architectural direction. From 06bef8e5a386c18d783d71b6583d9037fa9db660 Mon Sep 17 00:00:00 2001 From: Suraj Singh Date: Tue, 23 Aug 2022 15:50:26 -0700 Subject: [PATCH 007/187] [Segment Replication] Add update doc integ test for Segment replication (#4234) Signed-off-by: Suraj Singh Signed-off-by: Suraj Singh --- .../replication/SegmentReplicationIT.java | 60 ++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/replication/SegmentReplicationIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/replication/SegmentReplicationIT.java index 8566cc5556861..a9b6787d87bdf 100644 --- a/server/src/internalClusterTest/java/org/opensearch/indices/replication/SegmentReplicationIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/indices/replication/SegmentReplicationIT.java @@ -16,6 +16,8 @@ import org.opensearch.action.admin.indices.segments.IndicesSegmentsRequest; import org.opensearch.action.admin.indices.segments.ShardSegments; import org.opensearch.action.support.WriteRequest; +import org.opensearch.action.update.UpdateResponse; +import org.opensearch.client.Requests; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.node.DiscoveryNode; @@ -46,8 +48,10 @@ import java.util.function.Function; import java.util.stream.Collectors; -import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; +import static org.opensearch.index.query.QueryBuilders.matchQuery; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertSearchHits; @OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) public class SegmentReplicationIT extends OpenSearchIntegTestCase { @@ -419,6 +423,60 @@ public void testDeleteOperations() throws Exception { } } + public void testUpdateOperations() throws Exception { + final String primary = internalCluster().startNode(); + createIndex(INDEX_NAME); + ensureYellow(INDEX_NAME); + final String replica = internalCluster().startNode(); + + final int initialDocCount = scaledRandomIntBetween(0, 200); + try ( + BackgroundIndexer indexer = new BackgroundIndexer( + INDEX_NAME, + "_doc", + client(), + -1, + RandomizedTest.scaledRandomIntBetween(2, 5), + false, + random() + ) + ) { + indexer.start(initialDocCount); + waitForDocs(initialDocCount, indexer); + refresh(INDEX_NAME); + waitForReplicaUpdate(); + + // wait a short amount of time to give replication a chance to complete. + assertHitCount(client(primary).prepareSearch(INDEX_NAME).setSize(0).setPreference("_only_local").get(), initialDocCount); + assertHitCount(client(replica).prepareSearch(INDEX_NAME).setSize(0).setPreference("_only_local").get(), initialDocCount); + + final int additionalDocCount = scaledRandomIntBetween(0, 200); + final int expectedHitCount = initialDocCount + additionalDocCount; + indexer.start(additionalDocCount); + waitForDocs(expectedHitCount, indexer); + waitForReplicaUpdate(); + + assertHitCount(client(primary).prepareSearch(INDEX_NAME).setSize(0).setPreference("_only_local").get(), expectedHitCount); + assertHitCount(client(replica).prepareSearch(INDEX_NAME).setSize(0).setPreference("_only_local").get(), expectedHitCount); + + Set ids = indexer.getIds(); + String id = ids.toArray()[0].toString(); + UpdateResponse updateResponse = client(primary).prepareUpdate(INDEX_NAME, id) + .setDoc(Requests.INDEX_CONTENT_TYPE, "foo", "baz") + .setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL) + .get(); + assertFalse("request shouldn't have forced a refresh", updateResponse.forcedRefresh()); + assertEquals(2, updateResponse.getVersion()); + + refresh(INDEX_NAME); + waitForReplicaUpdate(); + + assertSearchHits(client(primary).prepareSearch(INDEX_NAME).setQuery(matchQuery("foo", "baz")).get(), id); + assertSearchHits(client(replica).prepareSearch(INDEX_NAME).setQuery(matchQuery("foo", "baz")).get(), id); + + } + } + private void assertSegmentStats(int numberOfReplicas) throws IOException { final IndicesSegmentResponse indicesSegmentResponse = client().admin().indices().segments(new IndicesSegmentsRequest()).actionGet(); From a3fb105190f5a7a38095075eb6ab2c8cbeef870e Mon Sep 17 00:00:00 2001 From: Alex Burck Date: Wed, 24 Aug 2022 10:27:23 -0500 Subject: [PATCH 008/187] [BUG] Running "opensearch-service.bat start" and "opensearch-service.bat manager" (#4289) * [BUG] Update opensearch-service-x64.exe parameters to //ES for Execute Service. Update opensearch-service-mgr.exe parameters to //ES for Edit Service. Add code comments for the Apache Commons Daemon. Signed-off-by: Alex Burck * update changelog with pull request link Signed-off-by: Alex Burck Signed-off-by: Alex Burck --- CHANGELOG.md | 1 + distribution/src/bin/opensearch-service.bat | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a02bfdaf0320..513fb92ad2675 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### Removed ### Fixed +- `opensearch-service.bat start` and `opensearch-service.bat manager` failing to run ([#4289](https://github.com/opensearch-project/OpenSearch/pull/4289)) ### Security diff --git a/distribution/src/bin/opensearch-service.bat b/distribution/src/bin/opensearch-service.bat index 4dd8356340d10..8b91d806ef64f 100644 --- a/distribution/src/bin/opensearch-service.bat +++ b/distribution/src/bin/opensearch-service.bat @@ -8,6 +8,10 @@ if /i "%1" == "install" set NOJAVA= call "%~dp0opensearch-env.bat" %NOJAVA% || exit /b 1 +rem opensearch-service-x64.exe is based off of the Apache Commons Daemon procrun service application. +rem Run "opensearch-service-x64.exe version" for version information. +rem Run "opensearch-service-x64.exe help" for command options. +rem See https://commons.apache.org/proper/commons-daemon/procrun.html for more information. set EXECUTABLE=%OPENSEARCH_HOME%\bin\opensearch-service-x64.exe if "%SERVICE_ID%" == "" set SERVICE_ID=opensearch-service-x64 set ARCH=64-bit @@ -45,7 +49,8 @@ echo Usage: opensearch-service.bat install^|remove^|start^|stop^|manager [SERVIC goto:eof :doStart -"%EXECUTABLE%" //OPENSEARCH//%SERVICE_ID% %LOG_OPTS% +rem //ES == Execute Service +"%EXECUTABLE%" //ES//%SERVICE_ID% %LOG_OPTS% if not errorlevel 1 goto started echo Failed starting '%SERVICE_ID%' service exit /B 1 @@ -55,6 +60,7 @@ echo The service '%SERVICE_ID%' has been started goto:eof :doStop +rem //SS == Stop Service "%EXECUTABLE%" //SS//%SERVICE_ID% %LOG_OPTS% if not errorlevel 1 goto stopped echo Failed stopping '%SERVICE_ID%' service @@ -65,8 +71,11 @@ echo The service '%SERVICE_ID%' has been stopped goto:eof :doManagment +rem opensearch-service-mgr.exe is based off of the Apache Commons Daemon procrun monitor application. +rem See https://commons.apache.org/proper/commons-daemon/procrun.html for more information. set EXECUTABLE_MGR=%OPENSEARCH_HOME%\bin\opensearch-service-mgr -"%EXECUTABLE_MGR%" //OPENSEARCH//%SERVICE_ID% +rem //ES == Edit Service +"%EXECUTABLE_MGR%" //ES//%SERVICE_ID% if not errorlevel 1 goto managed echo Failed starting service manager for '%SERVICE_ID%' exit /B 1 @@ -77,6 +86,7 @@ goto:eof :doRemove rem Remove the service +rem //DS == Delete Service "%EXECUTABLE%" //DS//%SERVICE_ID% %LOG_OPTS% if not errorlevel 1 goto removed echo Failed removing '%SERVICE_ID%' service @@ -207,6 +217,7 @@ if not "%SERVICE_USERNAME%" == "" ( set SERVICE_PARAMS=%SERVICE_PARAMS% --ServiceUser "%SERVICE_USERNAME%" --ServicePassword "%SERVICE_PASSWORD%" ) ) +rem //IS == Install Service "%EXECUTABLE%" //IS//%SERVICE_ID% --Startup %OPENSEARCH_START_TYPE% --StopTimeout %OPENSEARCH_STOP_TIMEOUT% --StartClass org.opensearch.bootstrap.OpenSearch --StartMethod main ++StartParams --quiet --StopClass org.opensearch.bootstrap.OpenSearch --StopMethod close --Classpath "%OPENSEARCH_CLASSPATH%" --JvmMs %JVM_MS% --JvmMx %JVM_MX% --JvmSs %JVM_SS% --JvmOptions %OTHER_JAVA_OPTS% ++JvmOptions %OPENSEARCH_PARAMS% %LOG_OPTS% --PidFile "%SERVICE_ID%.pid" --DisplayName "%SERVICE_DISPLAY_NAME%" --Description "%SERVICE_DESCRIPTION%" --Jvm "%JAVA_HOME%%JVM_DLL%" --StartMode jvm --StopMode jvm --StartPath "%OPENSEARCH_HOME%" %SERVICE_PARAMS% ++Environment HOSTNAME="%%COMPUTERNAME%%" if not errorlevel 1 goto installed From 0bf6b2ffc2152401bad2750532950eb0c12340c3 Mon Sep 17 00:00:00 2001 From: Marc Handalian Date: Wed, 24 Aug 2022 09:06:48 -0700 Subject: [PATCH 009/187] Removing dead code in RecoveryTarget. (#4278) * Removing dead code in RecoveryTarget. This code in RecoveryTarget is not invoked, all of these methods are implemented by the parent ReplicationTarget with the same behavior. Signed-off-by: Marc Handalian * PR Comments. Signed-off-by: Marc Handalian Signed-off-by: Marc Handalian --- .../indices/recovery/RecoveryTarget.java | 47 ------------------- .../replication/common/ReplicationTarget.java | 2 +- 2 files changed, 1 insertion(+), 48 deletions(-) diff --git a/server/src/main/java/org/opensearch/indices/recovery/RecoveryTarget.java b/server/src/main/java/org/opensearch/indices/recovery/RecoveryTarget.java index 652f3c9a55f53..7acc6b8b54fdd 100644 --- a/server/src/main/java/org/opensearch/indices/recovery/RecoveryTarget.java +++ b/server/src/main/java/org/opensearch/indices/recovery/RecoveryTarget.java @@ -177,51 +177,6 @@ public boolean reset(CancellableThreads newTargetCancellableThreads) throws IOEx return false; } - /** - * cancel the recovery. calling this method will clean temporary files and release the store - * unless this object is in use (in which case it will be cleaned once all ongoing users call - * {@link #decRef()} - *

- * if {@link #cancellableThreads()} was used, the threads will be interrupted. - */ - public void cancel(String reason) { - if (finished.compareAndSet(false, true)) { - try { - logger.debug("recovery canceled (reason: [{}])", reason); - cancellableThreads.cancel(reason); - } finally { - // release the initial reference. recovery files will be cleaned as soon as ref count goes to zero, potentially now - decRef(); - } - } - } - - /** - * fail the recovery and call listener - * - * @param e exception that encapsulating the failure - * @param sendShardFailure indicates whether to notify the cluster-manager of the shard failure - */ - public void fail(RecoveryFailedException e, boolean sendShardFailure) { - super.fail(e, sendShardFailure); - } - - /** mark the current recovery as done */ - public void markAsDone() { - if (finished.compareAndSet(false, true)) { - assert multiFileWriter.tempFileNames.isEmpty() : "not all temporary files are renamed"; - try { - // this might still throw an exception ie. if the shard is CLOSED due to some other event. - // it's safer to decrement the reference in a try finally here. - indexShard.postRecovery("peer recovery done"); - } finally { - // release the initial reference. recovery files will be cleaned as soon as ref count goes to zero, potentially now - decRef(); - } - listener.onDone(state()); - } - } - @Override protected void closeInternal() { try { @@ -246,8 +201,6 @@ protected String getPrefix() { @Override protected void onDone() { assert multiFileWriter.tempFileNames.isEmpty() : "not all temporary files are renamed"; - // this might still throw an exception ie. if the shard is CLOSED due to some other event. - // it's safer to decrement the reference in a try finally here. indexShard.postRecovery("peer recovery done"); } diff --git a/server/src/main/java/org/opensearch/indices/replication/common/ReplicationTarget.java b/server/src/main/java/org/opensearch/indices/replication/common/ReplicationTarget.java index 501ff46eeb2ff..42f4572fef3e4 100644 --- a/server/src/main/java/org/opensearch/indices/replication/common/ReplicationTarget.java +++ b/server/src/main/java/org/opensearch/indices/replication/common/ReplicationTarget.java @@ -155,7 +155,7 @@ public void markAsDone() { public void cancel(String reason) { if (finished.compareAndSet(false, true)) { try { - logger.debug("replication cancelled (reason: [{}])", reason); + logger.debug("replication/recovery cancelled (reason: [{}])", reason); onCancel(reason); } finally { // release the initial reference. replication files will be cleaned as soon as ref count goes to zero, potentially now From 5dd79479a2ca84a633a7583eb37df07504cd8a90 Mon Sep 17 00:00:00 2001 From: Kunal Kotwani Date: Wed, 24 Aug 2022 15:13:33 -0700 Subject: [PATCH 010/187] Update the head ref to changelog verifier (#4296) * Update changelog contribution guide Signed-off-by: Kunal Kotwani * Fix reference to pull request Signed-off-by: Kunal Kotwani Signed-off-by: Kunal Kotwani --- .github/workflows/changelog_verifier.yml | 2 +- CHANGELOG.md | 1 + CONTRIBUTING.md | 2 -- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/changelog_verifier.yml b/.github/workflows/changelog_verifier.yml index 505b02426f22c..ee9bf5e18d0d5 100644 --- a/.github/workflows/changelog_verifier.yml +++ b/.github/workflows/changelog_verifier.yml @@ -11,7 +11,7 @@ jobs: - uses: actions/checkout@v3 with: token: ${{ secrets.GITHUB_TOKEN }} - ref: ${{ github.event.pull_request.head.ref }} + ref: ${{ github.event.pull_request.head.sha }} - uses: dangoslen/dependabot-changelog-helper@v1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 513fb92ad2675..bc7baef8f83fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### Fixed - `opensearch-service.bat start` and `opensearch-service.bat manager` failing to run ([#4289](https://github.com/opensearch-project/OpenSearch/pull/4289)) +- PR reference to checkout code for changelog verifier ([#4296](https://github.com/opensearch-project/OpenSearch/pull/4296)) ### Security diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 16821b1915032..fc02d52f0bc3b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -131,8 +131,6 @@ As a contributor, you must ensure that every pull request has the changes listed Adding in the change is two step process - 1. Add your changes to the corresponding section within the CHANGELOG file with dummy pull request information, publish the PR - `Your change here ([#PR_NUMBER](PR_URL))` - 2. Update the entry for your change in [`CHANGELOG.md`](CHANGELOG.md) and make sure that you reference the pull request there. From 1bfabed0780c228f4f3c9a26aac2169e361c9426 Mon Sep 17 00:00:00 2001 From: Kunal Kotwani Date: Thu, 25 Aug 2022 15:17:16 -0700 Subject: [PATCH 011/187] Add 2.x version to CHANGELOG (#4297) Signed-off-by: Kunal Kotwani Signed-off-by: Kunal Kotwani --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc7baef8f83fa..c258100894555 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,5 +17,21 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### Security +## [2.x] +### Added +- Github workflow for changelog verification ([#4085](https://github.com/opensearch-project/OpenSearch/pull/4085)) + +### Changed + +### Deprecated + +### Removed + +### Fixed +- PR reference to checkout code for changelog verifier ([#4296](https://github.com/opensearch-project/OpenSearch/pull/4296)) + +### Security + [Unreleased]: https://github.com/opensearch-project/OpenSearch/compare/2.2.0...HEAD +[2.x]: https://github.com/opensearch-project/OpenSearch/compare/2.2.0...2.x From 1dbb63a8ee04812bd5f5ef5bc4995eac8b090438 Mon Sep 17 00:00:00 2001 From: Andrew Ross Date: Fri, 26 Aug 2022 12:58:47 -0700 Subject: [PATCH 012/187] Do not fail replica shard due to primary closure (#4133) This commit prevents a replica shard from being failed in the case that a replication action to a replica is canceled due to the primary shard being closed. Signed-off-by: Andrew Ross Signed-off-by: Andrew Ross --- CHANGELOG.md | 1 + .../index/store/CorruptedFileIT.java | 101 ++++++++++++++++++ .../org/opensearch/OpenSearchException.java | 7 ++ .../PendingReplicationActions.java | 14 ++- .../replication/TransportWriteAction.java | 24 +++-- .../shard/PrimaryShardClosedException.java | 26 +++++ .../ExceptionSerializationTests.java | 2 + .../PendingReplicationActionsTests.java | 3 +- .../TransportWriteActionTests.java | 45 ++++++++ 9 files changed, 205 insertions(+), 18 deletions(-) create mode 100644 server/src/main/java/org/opensearch/index/shard/PrimaryShardClosedException.java diff --git a/CHANGELOG.md b/CHANGELOG.md index c258100894555..e988435a688da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### Fixed - `opensearch-service.bat start` and `opensearch-service.bat manager` failing to run ([#4289](https://github.com/opensearch-project/OpenSearch/pull/4289)) - PR reference to checkout code for changelog verifier ([#4296](https://github.com/opensearch-project/OpenSearch/pull/4296)) +- Do not fail replica shard due to primary closure ([#4133](https://github.com/opensearch-project/OpenSearch/pull/4133)) ### Security diff --git a/server/src/internalClusterTest/java/org/opensearch/index/store/CorruptedFileIT.java b/server/src/internalClusterTest/java/org/opensearch/index/store/CorruptedFileIT.java index ee2067c591cef..960e17b76acb5 100644 --- a/server/src/internalClusterTest/java/org/opensearch/index/store/CorruptedFileIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/index/store/CorruptedFileIT.java @@ -40,6 +40,7 @@ import org.apache.lucene.store.FSDirectory; import org.apache.lucene.util.BytesRef; +import org.hamcrest.MatcherAssert; import org.opensearch.action.admin.cluster.health.ClusterHealthResponse; import org.opensearch.action.admin.cluster.node.stats.NodeStats; import org.opensearch.action.admin.cluster.node.stats.NodesStatsResponse; @@ -48,6 +49,7 @@ import org.opensearch.action.admin.indices.shards.IndicesShardStoresResponse; import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchResponse; +import org.opensearch.action.support.replication.TransportReplicationAction; import org.opensearch.client.Requests; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.health.ClusterHealthStatus; @@ -108,6 +110,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; import static org.opensearch.action.admin.cluster.node.stats.NodesStatsRequest.Metric.FS; import static org.opensearch.common.util.CollectionUtils.iterableAsArrayList; @@ -698,6 +701,104 @@ public void testReplicaCorruption() throws Exception { ensureGreen(TimeValue.timeValueSeconds(60)); } + public void testPrimaryCorruptionDuringReplicationDoesNotFailReplicaShard() throws Exception { + internalCluster().ensureAtLeastNumDataNodes(2); + final NodesStatsResponse nodeStats = client().admin().cluster().prepareNodesStats().get(); + final List dataNodeStats = nodeStats.getNodes() + .stream() + .filter(stat -> stat.getNode().isDataNode()) + .collect(Collectors.toUnmodifiableList()); + MatcherAssert.assertThat(dataNodeStats.size(), greaterThanOrEqualTo(2)); + + final NodeStats primaryNode = dataNodeStats.get(0); + final NodeStats replicaNode = dataNodeStats.get(1); + assertAcked( + prepareCreate("test").setSettings( + Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, "0") + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) + .put("index.routing.allocation.include._name", primaryNode.getNode().getName()) + .put(EnableAllocationDecider.INDEX_ROUTING_REBALANCE_ENABLE_SETTING.getKey(), EnableAllocationDecider.Rebalance.NONE) + .put("index.allocation.max_retries", Integer.MAX_VALUE) // keep on retrying + + ) + ); + ensureGreen(); + + // Add custom send behavior between primary and replica that will + // count down a latch to indicate that a replication operation is + // currently in flight, and then block on a second latch that will + // be released once the primary shard has been corrupted. + final CountDownLatch indexingInFlight = new CountDownLatch(1); + final CountDownLatch corruptionHasHappened = new CountDownLatch(1); + final MockTransportService mockTransportService = ((MockTransportService) internalCluster().getInstance( + TransportService.class, + primaryNode.getNode().getName() + )); + mockTransportService.addSendBehavior( + internalCluster().getInstance(TransportService.class, replicaNode.getNode().getName()), + (connection, requestId, action, request, options) -> { + if (request instanceof TransportReplicationAction.ConcreteShardRequest) { + indexingInFlight.countDown(); + try { + corruptionHasHappened.await(); + } catch (InterruptedException e) { + logger.info("Interrupted while waiting for corruption"); + } + } + connection.sendRequest(requestId, action, request, options); + } + ); + + // Configure the modified data node as a replica + final Settings build = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, "1") + .put("index.routing.allocation.include._name", primaryNode.getNode().getName() + "," + replicaNode.getNode().getName()) + .build(); + client().admin().indices().prepareUpdateSettings("test").setSettings(build).get(); + client().admin().cluster().prepareReroute().get(); + ensureGreen(); + + // Create a snapshot repository. This repo is used to take a snapshot after + // corrupting a file, which causes the node to notice the corrupt data and + // close the shard. + assertAcked( + client().admin() + .cluster() + .preparePutRepository("test-repo") + .setType("fs") + .setSettings( + Settings.builder() + .put("location", randomRepoPath().toAbsolutePath()) + .put("compress", randomBoolean()) + .put("chunk_size", randomIntBetween(100, 1000), ByteSizeUnit.BYTES) + ) + ); + + client().prepareIndex("test").setSource("field", "value").execute(); + indexingInFlight.await(); + + // Corrupt a file on the primary then take a snapshot. Snapshot should + // finish in the PARTIAL state since the corrupted file will cause a checksum + // validation failure. + final ShardRouting corruptedShardRouting = corruptRandomPrimaryFile(); + logger.info("--> {} corrupted", corruptedShardRouting); + final CreateSnapshotResponse createSnapshotResponse = client().admin() + .cluster() + .prepareCreateSnapshot("test-repo", "test-snap") + .setWaitForCompletion(true) + .setIndices("test") + .get(); + final SnapshotState snapshotState = createSnapshotResponse.getSnapshotInfo().state(); + MatcherAssert.assertThat("Expect file corruption to cause PARTIAL snapshot state", snapshotState, equalTo(SnapshotState.PARTIAL)); + + // Unblock the blocked indexing thread now that corruption on the primary has been confirmed + corruptionHasHappened.countDown(); + + // Assert the cluster returns to green status because the replica will be promoted to primary + ensureGreen(); + } + private int numShards(String... index) { ClusterState state = client().admin().cluster().prepareState().get().getState(); GroupShardsIterator shardIterators = state.getRoutingTable().activePrimaryShardsGrouped(index, false); diff --git a/server/src/main/java/org/opensearch/OpenSearchException.java b/server/src/main/java/org/opensearch/OpenSearchException.java index 4ebcd9622ce38..87efc03734d26 100644 --- a/server/src/main/java/org/opensearch/OpenSearchException.java +++ b/server/src/main/java/org/opensearch/OpenSearchException.java @@ -68,6 +68,7 @@ import static java.util.Collections.singletonMap; import static java.util.Collections.unmodifiableMap; import static org.opensearch.Version.V_2_1_0; +import static org.opensearch.Version.V_3_0_0; import static org.opensearch.cluster.metadata.IndexMetadata.INDEX_UUID_NA_VALUE; import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken; import static org.opensearch.common.xcontent.XContentParserUtils.ensureFieldName; @@ -1601,6 +1602,12 @@ private enum OpenSearchExceptionHandle { org.opensearch.indices.replication.common.ReplicationFailedException::new, 161, V_2_1_0 + ), + PRIMARY_SHARD_CLOSED_EXCEPTION( + org.opensearch.index.shard.PrimaryShardClosedException.class, + org.opensearch.index.shard.PrimaryShardClosedException::new, + 162, + V_3_0_0 ); final Class exceptionClass; diff --git a/server/src/main/java/org/opensearch/action/support/replication/PendingReplicationActions.java b/server/src/main/java/org/opensearch/action/support/replication/PendingReplicationActions.java index b305c4c8c83a7..7087b64758888 100644 --- a/server/src/main/java/org/opensearch/action/support/replication/PendingReplicationActions.java +++ b/server/src/main/java/org/opensearch/action/support/replication/PendingReplicationActions.java @@ -35,6 +35,7 @@ import org.opensearch.action.support.RetryableAction; import org.opensearch.common.lease.Releasable; import org.opensearch.common.util.concurrent.ConcurrentCollections; +import org.opensearch.index.shard.PrimaryShardClosedException; import org.opensearch.index.shard.IndexShardClosedException; import org.opensearch.index.shard.ReplicationGroup; import org.opensearch.index.shard.ShardId; @@ -45,6 +46,7 @@ import java.util.Map; import java.util.Set; import java.util.function.Consumer; +import java.util.function.Supplier; /** * Pending Replication Actions @@ -121,7 +123,7 @@ synchronized void acceptNewTrackedAllocationIds(Set trackedAllocationIds } } - cancelActions(toCancel, "Replica left ReplicationGroup"); + cancelActions(toCancel, () -> new IndexShardClosedException(shardId, "Replica left ReplicationGroup")); } @Override @@ -129,15 +131,11 @@ public synchronized void close() { ArrayList>> toCancel = new ArrayList<>(onGoingReplicationActions.values()); onGoingReplicationActions.clear(); - cancelActions(toCancel, "Primary closed."); + cancelActions(toCancel, () -> new PrimaryShardClosedException(shardId)); } - private void cancelActions(ArrayList>> toCancel, String message) { + private void cancelActions(ArrayList>> toCancel, Supplier exceptionSupplier) { threadPool.executor(ThreadPool.Names.GENERIC) - .execute( - () -> toCancel.stream() - .flatMap(Collection::stream) - .forEach(action -> action.cancel(new IndexShardClosedException(shardId, message))) - ); + .execute(() -> toCancel.stream().flatMap(Collection::stream).forEach(action -> action.cancel(exceptionSupplier.get()))); } } diff --git a/server/src/main/java/org/opensearch/action/support/replication/TransportWriteAction.java b/server/src/main/java/org/opensearch/action/support/replication/TransportWriteAction.java index 39fb89bc48568..7fc810808f560 100644 --- a/server/src/main/java/org/opensearch/action/support/replication/TransportWriteAction.java +++ b/server/src/main/java/org/opensearch/action/support/replication/TransportWriteAction.java @@ -52,6 +52,7 @@ import org.opensearch.index.IndexingPressureService; import org.opensearch.index.engine.Engine; import org.opensearch.index.mapper.MapperParsingException; +import org.opensearch.index.shard.PrimaryShardClosedException; import org.opensearch.index.shard.IndexShard; import org.opensearch.index.shard.ShardId; import org.opensearch.index.translog.Translog; @@ -514,15 +515,20 @@ public void failShardIfNeeded( if (TransportActions.isShardNotAvailableException(exception) == false) { logger.warn(new ParameterizedMessage("[{}] {}", replica.shardId(), message), exception); } - shardStateAction.remoteShardFailed( - replica.shardId(), - replica.allocationId().getId(), - primaryTerm, - true, - message, - exception, - listener - ); + // If a write action fails due to the closure of the primary shard + // then the replicas should not be marked as failed since they are + // still up-to-date with the (now closed) primary shard + if (exception instanceof PrimaryShardClosedException == false) { + shardStateAction.remoteShardFailed( + replica.shardId(), + replica.allocationId().getId(), + primaryTerm, + true, + message, + exception, + listener + ); + } } @Override diff --git a/server/src/main/java/org/opensearch/index/shard/PrimaryShardClosedException.java b/server/src/main/java/org/opensearch/index/shard/PrimaryShardClosedException.java new file mode 100644 index 0000000000000..d1b2bf9079289 --- /dev/null +++ b/server/src/main/java/org/opensearch/index/shard/PrimaryShardClosedException.java @@ -0,0 +1,26 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.index.shard; + +import java.io.IOException; + +import org.opensearch.common.io.stream.StreamInput; + +/** + * Exception to indicate failures are caused due to the closure of the primary + * shard. + * + * @opensearch.internal + */ +public class PrimaryShardClosedException extends IndexShardClosedException { + public PrimaryShardClosedException(ShardId shardId) { + super(shardId, "Primary closed"); + } + + public PrimaryShardClosedException(StreamInput in) throws IOException { + super(in); + } +} diff --git a/server/src/test/java/org/opensearch/ExceptionSerializationTests.java b/server/src/test/java/org/opensearch/ExceptionSerializationTests.java index 5a93d7c0bd86e..26b0ce7e9e20c 100644 --- a/server/src/test/java/org/opensearch/ExceptionSerializationTests.java +++ b/server/src/test/java/org/opensearch/ExceptionSerializationTests.java @@ -80,6 +80,7 @@ import org.opensearch.index.seqno.RetentionLeaseNotFoundException; import org.opensearch.index.shard.IllegalIndexShardStateException; import org.opensearch.index.shard.IndexShardState; +import org.opensearch.index.shard.PrimaryShardClosedException; import org.opensearch.index.shard.ShardId; import org.opensearch.index.shard.ShardNotInPrimaryModeException; import org.opensearch.indices.IndexTemplateMissingException; @@ -858,6 +859,7 @@ public void testIds() { ids.put(159, NodeHealthCheckFailureException.class); ids.put(160, NoSeedNodeLeftException.class); ids.put(161, ReplicationFailedException.class); + ids.put(162, PrimaryShardClosedException.class); Map, Integer> reverse = new HashMap<>(); for (Map.Entry> entry : ids.entrySet()) { diff --git a/server/src/test/java/org/opensearch/action/support/replication/PendingReplicationActionsTests.java b/server/src/test/java/org/opensearch/action/support/replication/PendingReplicationActionsTests.java index ec0cefed842cd..66d3b843529ab 100644 --- a/server/src/test/java/org/opensearch/action/support/replication/PendingReplicationActionsTests.java +++ b/server/src/test/java/org/opensearch/action/support/replication/PendingReplicationActionsTests.java @@ -38,6 +38,7 @@ import org.opensearch.common.UUIDs; import org.opensearch.common.unit.TimeValue; import org.opensearch.index.shard.IndexShardClosedException; +import org.opensearch.index.shard.PrimaryShardClosedException; import org.opensearch.index.shard.ShardId; import org.opensearch.test.OpenSearchTestCase; import org.opensearch.threadpool.TestThreadPool; @@ -102,7 +103,7 @@ public void testAllocationIdActionWillBeCancelledOnClose() { pendingReplication.addPendingAction(allocationId, action); action.run(); pendingReplication.close(); - expectThrows(IndexShardClosedException.class, future::actionGet); + expectThrows(PrimaryShardClosedException.class, future::actionGet); } private class TestAction extends RetryableAction { diff --git a/server/src/test/java/org/opensearch/action/support/replication/TransportWriteActionTests.java b/server/src/test/java/org/opensearch/action/support/replication/TransportWriteActionTests.java index 4da32a890fd0e..137aca4966936 100644 --- a/server/src/test/java/org/opensearch/action/support/replication/TransportWriteActionTests.java +++ b/server/src/test/java/org/opensearch/action/support/replication/TransportWriteActionTests.java @@ -32,6 +32,7 @@ package org.opensearch.action.support.replication; +import org.hamcrest.MatcherAssert; import org.opensearch.OpenSearchException; import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; @@ -57,6 +58,7 @@ import org.opensearch.index.IndexService; import org.opensearch.index.IndexingPressureService; import org.opensearch.index.shard.IndexShard; +import org.opensearch.index.shard.PrimaryShardClosedException; import org.opensearch.index.shard.ShardId; import org.opensearch.index.shard.ShardNotFoundException; import org.opensearch.index.translog.Translog; @@ -91,6 +93,7 @@ import java.util.stream.Collectors; import static java.util.Collections.emptyMap; +import static org.hamcrest.Matchers.emptyArray; import static org.opensearch.test.ClusterServiceUtils.createClusterService; import static org.hamcrest.Matchers.arrayWithSize; import static org.hamcrest.Matchers.equalTo; @@ -395,6 +398,48 @@ public void testReplicaProxy() throws InterruptedException, ExecutionException { } } + public void testPrimaryClosedDoesNotFailShard() { + final CapturingTransport transport = new CapturingTransport(); + final TransportService transportService = transport.createTransportService( + clusterService.getSettings(), + threadPool, + TransportService.NOOP_TRANSPORT_INTERCEPTOR, + x -> clusterService.localNode(), + null, + Collections.emptySet() + ); + transportService.start(); + transportService.acceptIncomingRequests(); + final ShardStateAction shardStateAction = new ShardStateAction(clusterService, transportService, null, null, threadPool); + final TestAction action = new TestAction( + Settings.EMPTY, + "internal:testAction", + transportService, + clusterService, + shardStateAction, + threadPool + ); + final String index = "test"; + final ShardId shardId = new ShardId(index, "_na_", 0); + final ClusterState state = ClusterStateCreationUtils.stateWithActivePrimary(index, true, 1, 0); + ClusterServiceUtils.setState(clusterService, state); + final long primaryTerm = state.metadata().index(index).primaryTerm(0); + final ShardRouting shardRouting = state.routingTable().shardRoutingTable(shardId).replicaShards().get(0); + + // Assert that failShardIfNeeded is a no-op for the PrimaryShardClosedException failure + final AtomicInteger callbackCount = new AtomicInteger(0); + action.newReplicasProxy() + .failShardIfNeeded( + shardRouting, + primaryTerm, + "test", + new PrimaryShardClosedException(shardId), + ActionListener.wrap(callbackCount::incrementAndGet) + ); + MatcherAssert.assertThat(transport.getCapturedRequestsAndClear(), emptyArray()); + MatcherAssert.assertThat(callbackCount.get(), equalTo(0)); + } + private class TestAction extends TransportWriteAction { private final boolean withDocumentFailureOnPrimary; From c62cecb048bafe8b79709660956fe4cba5548872 Mon Sep 17 00:00:00 2001 From: Andriy Redko Date: Fri, 26 Aug 2022 17:46:01 -0400 Subject: [PATCH 013/187] Some dependency updates (#4308) Signed-off-by: Andriy Redko Signed-off-by: Andriy Redko --- CHANGELOG.md | 1 + buildSrc/version.properties | 10 +++++----- client/rest/licenses/commons-codec-1.13.jar.sha1 | 1 - client/rest/licenses/commons-codec-1.15.jar.sha1 | 1 + client/rest/licenses/httpasyncclient-4.1.4.jar.sha1 | 1 - client/rest/licenses/httpasyncclient-4.1.5.jar.sha1 | 1 + client/rest/licenses/httpcore-4.4.12.jar.sha1 | 1 - client/rest/licenses/httpcore-4.4.15.jar.sha1 | 1 + client/rest/licenses/httpcore-nio-4.4.12.jar.sha1 | 1 - client/rest/licenses/httpcore-nio-4.4.15.jar.sha1 | 1 + client/sniffer/licenses/commons-codec-1.13.jar.sha1 | 1 - client/sniffer/licenses/commons-codec-1.15.jar.sha1 | 1 + client/sniffer/licenses/httpcore-4.4.12.jar.sha1 | 1 - client/sniffer/licenses/httpcore-4.4.15.jar.sha1 | 1 + .../licenses/commons-codec-1.13.jar.sha1 | 1 - .../licenses/commons-codec-1.15.jar.sha1 | 1 + .../licenses/commons-codec-1.13.jar.sha1 | 1 - .../licenses/commons-codec-1.15.jar.sha1 | 1 + .../licenses/httpcore-4.4.12.jar.sha1 | 1 - .../licenses/httpcore-4.4.15.jar.sha1 | 1 + .../discovery-ec2/licenses/commons-codec-1.13.jar.sha1 | 1 - .../discovery-ec2/licenses/commons-codec-1.15.jar.sha1 | 1 + .../discovery-ec2/licenses/httpcore-4.4.12.jar.sha1 | 1 - .../discovery-ec2/licenses/httpcore-4.4.15.jar.sha1 | 1 + .../discovery-gce/licenses/commons-codec-1.13.jar.sha1 | 1 - .../discovery-gce/licenses/commons-codec-1.15.jar.sha1 | 1 + .../discovery-gce/licenses/httpcore-4.4.12.jar.sha1 | 1 - .../discovery-gce/licenses/httpcore-4.4.15.jar.sha1 | 1 + .../licenses/commons-codec-1.13.jar.sha1 | 1 - .../licenses/commons-codec-1.15.jar.sha1 | 1 + .../licenses/slf4j-api-1.6.2.jar.sha1 | 1 - .../licenses/slf4j-api-1.7.36.jar.sha1 | 1 + .../repository-azure/licenses/slf4j-api-1.6.2.jar.sha1 | 1 - .../licenses/slf4j-api-1.7.36.jar.sha1 | 1 + .../licenses/commons-codec-1.13.jar.sha1 | 1 - .../licenses/commons-codec-1.15.jar.sha1 | 1 + .../licenses/commons-codec-1.13.jar.sha1 | 1 - .../licenses/commons-codec-1.15.jar.sha1 | 1 + .../repository-hdfs/licenses/slf4j-api-1.6.2.jar.sha1 | 1 - .../repository-hdfs/licenses/slf4j-api-1.7.36.jar.sha1 | 1 + .../repository-s3/licenses/commons-codec-1.13.jar.sha1 | 1 - .../repository-s3/licenses/commons-codec-1.15.jar.sha1 | 1 + .../repository-s3/licenses/httpcore-4.4.12.jar.sha1 | 1 - .../repository-s3/licenses/httpcore-4.4.15.jar.sha1 | 1 + 44 files changed, 27 insertions(+), 26 deletions(-) delete mode 100644 client/rest/licenses/commons-codec-1.13.jar.sha1 create mode 100644 client/rest/licenses/commons-codec-1.15.jar.sha1 delete mode 100644 client/rest/licenses/httpasyncclient-4.1.4.jar.sha1 create mode 100644 client/rest/licenses/httpasyncclient-4.1.5.jar.sha1 delete mode 100644 client/rest/licenses/httpcore-4.4.12.jar.sha1 create mode 100644 client/rest/licenses/httpcore-4.4.15.jar.sha1 delete mode 100644 client/rest/licenses/httpcore-nio-4.4.12.jar.sha1 create mode 100644 client/rest/licenses/httpcore-nio-4.4.15.jar.sha1 delete mode 100644 client/sniffer/licenses/commons-codec-1.13.jar.sha1 create mode 100644 client/sniffer/licenses/commons-codec-1.15.jar.sha1 delete mode 100644 client/sniffer/licenses/httpcore-4.4.12.jar.sha1 create mode 100644 client/sniffer/licenses/httpcore-4.4.15.jar.sha1 delete mode 100644 plugins/analysis-phonetic/licenses/commons-codec-1.13.jar.sha1 create mode 100644 plugins/analysis-phonetic/licenses/commons-codec-1.15.jar.sha1 delete mode 100644 plugins/discovery-azure-classic/licenses/commons-codec-1.13.jar.sha1 create mode 100644 plugins/discovery-azure-classic/licenses/commons-codec-1.15.jar.sha1 delete mode 100644 plugins/discovery-azure-classic/licenses/httpcore-4.4.12.jar.sha1 create mode 100644 plugins/discovery-azure-classic/licenses/httpcore-4.4.15.jar.sha1 delete mode 100644 plugins/discovery-ec2/licenses/commons-codec-1.13.jar.sha1 create mode 100644 plugins/discovery-ec2/licenses/commons-codec-1.15.jar.sha1 delete mode 100644 plugins/discovery-ec2/licenses/httpcore-4.4.12.jar.sha1 create mode 100644 plugins/discovery-ec2/licenses/httpcore-4.4.15.jar.sha1 delete mode 100644 plugins/discovery-gce/licenses/commons-codec-1.13.jar.sha1 create mode 100644 plugins/discovery-gce/licenses/commons-codec-1.15.jar.sha1 delete mode 100644 plugins/discovery-gce/licenses/httpcore-4.4.12.jar.sha1 create mode 100644 plugins/discovery-gce/licenses/httpcore-4.4.15.jar.sha1 delete mode 100644 plugins/ingest-attachment/licenses/commons-codec-1.13.jar.sha1 create mode 100644 plugins/ingest-attachment/licenses/commons-codec-1.15.jar.sha1 delete mode 100644 plugins/ingest-attachment/licenses/slf4j-api-1.6.2.jar.sha1 create mode 100644 plugins/ingest-attachment/licenses/slf4j-api-1.7.36.jar.sha1 delete mode 100644 plugins/repository-azure/licenses/slf4j-api-1.6.2.jar.sha1 create mode 100644 plugins/repository-azure/licenses/slf4j-api-1.7.36.jar.sha1 delete mode 100644 plugins/repository-gcs/licenses/commons-codec-1.13.jar.sha1 create mode 100644 plugins/repository-gcs/licenses/commons-codec-1.15.jar.sha1 delete mode 100644 plugins/repository-hdfs/licenses/commons-codec-1.13.jar.sha1 create mode 100644 plugins/repository-hdfs/licenses/commons-codec-1.15.jar.sha1 delete mode 100644 plugins/repository-hdfs/licenses/slf4j-api-1.6.2.jar.sha1 create mode 100644 plugins/repository-hdfs/licenses/slf4j-api-1.7.36.jar.sha1 delete mode 100644 plugins/repository-s3/licenses/commons-codec-1.13.jar.sha1 create mode 100644 plugins/repository-s3/licenses/commons-codec-1.15.jar.sha1 delete mode 100644 plugins/repository-s3/licenses/httpcore-4.4.12.jar.sha1 create mode 100644 plugins/repository-s3/licenses/httpcore-4.4.15.jar.sha1 diff --git a/CHANGELOG.md b/CHANGELOG.md index e988435a688da..26ff011609635 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Github workflow for changelog verification ([#4085](https://github.com/opensearch-project/OpenSearch/pull/4085)) ### Changed + - Dependency updates (httpcore, mockito, slf4j, httpasyncclient, commons-codec) ([#4308](https://github.com/opensearch-project/OpenSearch/pull/4308)) ### Deprecated diff --git a/buildSrc/version.properties b/buildSrc/version.properties index 4af1acfed0ab2..876910d5351d0 100644 --- a/buildSrc/version.properties +++ b/buildSrc/version.properties @@ -15,7 +15,7 @@ snakeyaml = 1.26 icu4j = 70.1 supercsv = 2.4.0 log4j = 2.17.1 -slf4j = 1.6.2 +slf4j = 1.7.36 asm = 9.3 # when updating the JNA version, also update the version in buildSrc/build.gradle @@ -26,10 +26,10 @@ joda = 2.10.13 # client dependencies httpclient = 4.5.13 -httpcore = 4.4.12 -httpasyncclient = 4.1.4 +httpcore = 4.4.15 +httpasyncclient = 4.1.5 commonslogging = 1.2 -commonscodec = 1.13 +commonscodec = 1.15 # plugin dependencies aws = 1.12.270 @@ -42,7 +42,7 @@ bouncycastle=1.70 randomizedrunner = 2.7.1 junit = 4.13.2 hamcrest = 2.1 -mockito = 4.6.1 +mockito = 4.7.0 objenesis = 3.2 bytebuddy = 1.12.12 diff --git a/client/rest/licenses/commons-codec-1.13.jar.sha1 b/client/rest/licenses/commons-codec-1.13.jar.sha1 deleted file mode 100644 index 66b72c414d63a..0000000000000 --- a/client/rest/licenses/commons-codec-1.13.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -3f18e1aa31031d89db6f01ba05d501258ce69d2c \ No newline at end of file diff --git a/client/rest/licenses/commons-codec-1.15.jar.sha1 b/client/rest/licenses/commons-codec-1.15.jar.sha1 new file mode 100644 index 0000000000000..62d99837b87e1 --- /dev/null +++ b/client/rest/licenses/commons-codec-1.15.jar.sha1 @@ -0,0 +1 @@ +49d94806b6e3dc933dacbd8acb0fdbab8ebd1e5d \ No newline at end of file diff --git a/client/rest/licenses/httpasyncclient-4.1.4.jar.sha1 b/client/rest/licenses/httpasyncclient-4.1.4.jar.sha1 deleted file mode 100644 index 8360ab45c7ab3..0000000000000 --- a/client/rest/licenses/httpasyncclient-4.1.4.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -f3a3240681faae3fa46b573a4c7e50cec9db0d86 \ No newline at end of file diff --git a/client/rest/licenses/httpasyncclient-4.1.5.jar.sha1 b/client/rest/licenses/httpasyncclient-4.1.5.jar.sha1 new file mode 100644 index 0000000000000..366a9e31069a6 --- /dev/null +++ b/client/rest/licenses/httpasyncclient-4.1.5.jar.sha1 @@ -0,0 +1 @@ +cd18227f1eb8e9a263286c1d7362ceb24f6f9b32 \ No newline at end of file diff --git a/client/rest/licenses/httpcore-4.4.12.jar.sha1 b/client/rest/licenses/httpcore-4.4.12.jar.sha1 deleted file mode 100644 index 3c046171b30da..0000000000000 --- a/client/rest/licenses/httpcore-4.4.12.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -21ebaf6d532bc350ba95bd81938fa5f0e511c132 \ No newline at end of file diff --git a/client/rest/licenses/httpcore-4.4.15.jar.sha1 b/client/rest/licenses/httpcore-4.4.15.jar.sha1 new file mode 100644 index 0000000000000..42a03b5d7a376 --- /dev/null +++ b/client/rest/licenses/httpcore-4.4.15.jar.sha1 @@ -0,0 +1 @@ +7f2e0c573eaa7a74bac2e89b359e1f73d92a0a1d \ No newline at end of file diff --git a/client/rest/licenses/httpcore-nio-4.4.12.jar.sha1 b/client/rest/licenses/httpcore-nio-4.4.12.jar.sha1 deleted file mode 100644 index 4de932dc5aca0..0000000000000 --- a/client/rest/licenses/httpcore-nio-4.4.12.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -84cd29eca842f31db02987cfedea245af020198b \ No newline at end of file diff --git a/client/rest/licenses/httpcore-nio-4.4.15.jar.sha1 b/client/rest/licenses/httpcore-nio-4.4.15.jar.sha1 new file mode 100644 index 0000000000000..251b35ab6a1a5 --- /dev/null +++ b/client/rest/licenses/httpcore-nio-4.4.15.jar.sha1 @@ -0,0 +1 @@ +85d2b6825d42db909a1474f0ffbd6328429b7a32 \ No newline at end of file diff --git a/client/sniffer/licenses/commons-codec-1.13.jar.sha1 b/client/sniffer/licenses/commons-codec-1.13.jar.sha1 deleted file mode 100644 index 66b72c414d63a..0000000000000 --- a/client/sniffer/licenses/commons-codec-1.13.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -3f18e1aa31031d89db6f01ba05d501258ce69d2c \ No newline at end of file diff --git a/client/sniffer/licenses/commons-codec-1.15.jar.sha1 b/client/sniffer/licenses/commons-codec-1.15.jar.sha1 new file mode 100644 index 0000000000000..62d99837b87e1 --- /dev/null +++ b/client/sniffer/licenses/commons-codec-1.15.jar.sha1 @@ -0,0 +1 @@ +49d94806b6e3dc933dacbd8acb0fdbab8ebd1e5d \ No newline at end of file diff --git a/client/sniffer/licenses/httpcore-4.4.12.jar.sha1 b/client/sniffer/licenses/httpcore-4.4.12.jar.sha1 deleted file mode 100644 index 3c046171b30da..0000000000000 --- a/client/sniffer/licenses/httpcore-4.4.12.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -21ebaf6d532bc350ba95bd81938fa5f0e511c132 \ No newline at end of file diff --git a/client/sniffer/licenses/httpcore-4.4.15.jar.sha1 b/client/sniffer/licenses/httpcore-4.4.15.jar.sha1 new file mode 100644 index 0000000000000..42a03b5d7a376 --- /dev/null +++ b/client/sniffer/licenses/httpcore-4.4.15.jar.sha1 @@ -0,0 +1 @@ +7f2e0c573eaa7a74bac2e89b359e1f73d92a0a1d \ No newline at end of file diff --git a/plugins/analysis-phonetic/licenses/commons-codec-1.13.jar.sha1 b/plugins/analysis-phonetic/licenses/commons-codec-1.13.jar.sha1 deleted file mode 100644 index 66b72c414d63a..0000000000000 --- a/plugins/analysis-phonetic/licenses/commons-codec-1.13.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -3f18e1aa31031d89db6f01ba05d501258ce69d2c \ No newline at end of file diff --git a/plugins/analysis-phonetic/licenses/commons-codec-1.15.jar.sha1 b/plugins/analysis-phonetic/licenses/commons-codec-1.15.jar.sha1 new file mode 100644 index 0000000000000..62d99837b87e1 --- /dev/null +++ b/plugins/analysis-phonetic/licenses/commons-codec-1.15.jar.sha1 @@ -0,0 +1 @@ +49d94806b6e3dc933dacbd8acb0fdbab8ebd1e5d \ No newline at end of file diff --git a/plugins/discovery-azure-classic/licenses/commons-codec-1.13.jar.sha1 b/plugins/discovery-azure-classic/licenses/commons-codec-1.13.jar.sha1 deleted file mode 100644 index 66b72c414d63a..0000000000000 --- a/plugins/discovery-azure-classic/licenses/commons-codec-1.13.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -3f18e1aa31031d89db6f01ba05d501258ce69d2c \ No newline at end of file diff --git a/plugins/discovery-azure-classic/licenses/commons-codec-1.15.jar.sha1 b/plugins/discovery-azure-classic/licenses/commons-codec-1.15.jar.sha1 new file mode 100644 index 0000000000000..62d99837b87e1 --- /dev/null +++ b/plugins/discovery-azure-classic/licenses/commons-codec-1.15.jar.sha1 @@ -0,0 +1 @@ +49d94806b6e3dc933dacbd8acb0fdbab8ebd1e5d \ No newline at end of file diff --git a/plugins/discovery-azure-classic/licenses/httpcore-4.4.12.jar.sha1 b/plugins/discovery-azure-classic/licenses/httpcore-4.4.12.jar.sha1 deleted file mode 100644 index 3c046171b30da..0000000000000 --- a/plugins/discovery-azure-classic/licenses/httpcore-4.4.12.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -21ebaf6d532bc350ba95bd81938fa5f0e511c132 \ No newline at end of file diff --git a/plugins/discovery-azure-classic/licenses/httpcore-4.4.15.jar.sha1 b/plugins/discovery-azure-classic/licenses/httpcore-4.4.15.jar.sha1 new file mode 100644 index 0000000000000..42a03b5d7a376 --- /dev/null +++ b/plugins/discovery-azure-classic/licenses/httpcore-4.4.15.jar.sha1 @@ -0,0 +1 @@ +7f2e0c573eaa7a74bac2e89b359e1f73d92a0a1d \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/commons-codec-1.13.jar.sha1 b/plugins/discovery-ec2/licenses/commons-codec-1.13.jar.sha1 deleted file mode 100644 index 66b72c414d63a..0000000000000 --- a/plugins/discovery-ec2/licenses/commons-codec-1.13.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -3f18e1aa31031d89db6f01ba05d501258ce69d2c \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/commons-codec-1.15.jar.sha1 b/plugins/discovery-ec2/licenses/commons-codec-1.15.jar.sha1 new file mode 100644 index 0000000000000..62d99837b87e1 --- /dev/null +++ b/plugins/discovery-ec2/licenses/commons-codec-1.15.jar.sha1 @@ -0,0 +1 @@ +49d94806b6e3dc933dacbd8acb0fdbab8ebd1e5d \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/httpcore-4.4.12.jar.sha1 b/plugins/discovery-ec2/licenses/httpcore-4.4.12.jar.sha1 deleted file mode 100644 index 3c046171b30da..0000000000000 --- a/plugins/discovery-ec2/licenses/httpcore-4.4.12.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -21ebaf6d532bc350ba95bd81938fa5f0e511c132 \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/httpcore-4.4.15.jar.sha1 b/plugins/discovery-ec2/licenses/httpcore-4.4.15.jar.sha1 new file mode 100644 index 0000000000000..42a03b5d7a376 --- /dev/null +++ b/plugins/discovery-ec2/licenses/httpcore-4.4.15.jar.sha1 @@ -0,0 +1 @@ +7f2e0c573eaa7a74bac2e89b359e1f73d92a0a1d \ No newline at end of file diff --git a/plugins/discovery-gce/licenses/commons-codec-1.13.jar.sha1 b/plugins/discovery-gce/licenses/commons-codec-1.13.jar.sha1 deleted file mode 100644 index 66b72c414d63a..0000000000000 --- a/plugins/discovery-gce/licenses/commons-codec-1.13.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -3f18e1aa31031d89db6f01ba05d501258ce69d2c \ No newline at end of file diff --git a/plugins/discovery-gce/licenses/commons-codec-1.15.jar.sha1 b/plugins/discovery-gce/licenses/commons-codec-1.15.jar.sha1 new file mode 100644 index 0000000000000..62d99837b87e1 --- /dev/null +++ b/plugins/discovery-gce/licenses/commons-codec-1.15.jar.sha1 @@ -0,0 +1 @@ +49d94806b6e3dc933dacbd8acb0fdbab8ebd1e5d \ No newline at end of file diff --git a/plugins/discovery-gce/licenses/httpcore-4.4.12.jar.sha1 b/plugins/discovery-gce/licenses/httpcore-4.4.12.jar.sha1 deleted file mode 100644 index 3c046171b30da..0000000000000 --- a/plugins/discovery-gce/licenses/httpcore-4.4.12.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -21ebaf6d532bc350ba95bd81938fa5f0e511c132 \ No newline at end of file diff --git a/plugins/discovery-gce/licenses/httpcore-4.4.15.jar.sha1 b/plugins/discovery-gce/licenses/httpcore-4.4.15.jar.sha1 new file mode 100644 index 0000000000000..42a03b5d7a376 --- /dev/null +++ b/plugins/discovery-gce/licenses/httpcore-4.4.15.jar.sha1 @@ -0,0 +1 @@ +7f2e0c573eaa7a74bac2e89b359e1f73d92a0a1d \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/commons-codec-1.13.jar.sha1 b/plugins/ingest-attachment/licenses/commons-codec-1.13.jar.sha1 deleted file mode 100644 index 66b72c414d63a..0000000000000 --- a/plugins/ingest-attachment/licenses/commons-codec-1.13.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -3f18e1aa31031d89db6f01ba05d501258ce69d2c \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/commons-codec-1.15.jar.sha1 b/plugins/ingest-attachment/licenses/commons-codec-1.15.jar.sha1 new file mode 100644 index 0000000000000..62d99837b87e1 --- /dev/null +++ b/plugins/ingest-attachment/licenses/commons-codec-1.15.jar.sha1 @@ -0,0 +1 @@ +49d94806b6e3dc933dacbd8acb0fdbab8ebd1e5d \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/slf4j-api-1.6.2.jar.sha1 b/plugins/ingest-attachment/licenses/slf4j-api-1.6.2.jar.sha1 deleted file mode 100644 index a2f93ea55802b..0000000000000 --- a/plugins/ingest-attachment/licenses/slf4j-api-1.6.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -8619e95939167fb37245b5670135e4feb0ec7d50 \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/slf4j-api-1.7.36.jar.sha1 b/plugins/ingest-attachment/licenses/slf4j-api-1.7.36.jar.sha1 new file mode 100644 index 0000000000000..77b9917528382 --- /dev/null +++ b/plugins/ingest-attachment/licenses/slf4j-api-1.7.36.jar.sha1 @@ -0,0 +1 @@ +6c62681a2f655b49963a5983b8b0950a6120ae14 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/slf4j-api-1.6.2.jar.sha1 b/plugins/repository-azure/licenses/slf4j-api-1.6.2.jar.sha1 deleted file mode 100644 index a2f93ea55802b..0000000000000 --- a/plugins/repository-azure/licenses/slf4j-api-1.6.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -8619e95939167fb37245b5670135e4feb0ec7d50 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/slf4j-api-1.7.36.jar.sha1 b/plugins/repository-azure/licenses/slf4j-api-1.7.36.jar.sha1 new file mode 100644 index 0000000000000..77b9917528382 --- /dev/null +++ b/plugins/repository-azure/licenses/slf4j-api-1.7.36.jar.sha1 @@ -0,0 +1 @@ +6c62681a2f655b49963a5983b8b0950a6120ae14 \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/commons-codec-1.13.jar.sha1 b/plugins/repository-gcs/licenses/commons-codec-1.13.jar.sha1 deleted file mode 100644 index 66b72c414d63a..0000000000000 --- a/plugins/repository-gcs/licenses/commons-codec-1.13.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -3f18e1aa31031d89db6f01ba05d501258ce69d2c \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/commons-codec-1.15.jar.sha1 b/plugins/repository-gcs/licenses/commons-codec-1.15.jar.sha1 new file mode 100644 index 0000000000000..62d99837b87e1 --- /dev/null +++ b/plugins/repository-gcs/licenses/commons-codec-1.15.jar.sha1 @@ -0,0 +1 @@ +49d94806b6e3dc933dacbd8acb0fdbab8ebd1e5d \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/commons-codec-1.13.jar.sha1 b/plugins/repository-hdfs/licenses/commons-codec-1.13.jar.sha1 deleted file mode 100644 index 66b72c414d63a..0000000000000 --- a/plugins/repository-hdfs/licenses/commons-codec-1.13.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -3f18e1aa31031d89db6f01ba05d501258ce69d2c \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/commons-codec-1.15.jar.sha1 b/plugins/repository-hdfs/licenses/commons-codec-1.15.jar.sha1 new file mode 100644 index 0000000000000..62d99837b87e1 --- /dev/null +++ b/plugins/repository-hdfs/licenses/commons-codec-1.15.jar.sha1 @@ -0,0 +1 @@ +49d94806b6e3dc933dacbd8acb0fdbab8ebd1e5d \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/slf4j-api-1.6.2.jar.sha1 b/plugins/repository-hdfs/licenses/slf4j-api-1.6.2.jar.sha1 deleted file mode 100644 index a2f93ea55802b..0000000000000 --- a/plugins/repository-hdfs/licenses/slf4j-api-1.6.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -8619e95939167fb37245b5670135e4feb0ec7d50 \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/slf4j-api-1.7.36.jar.sha1 b/plugins/repository-hdfs/licenses/slf4j-api-1.7.36.jar.sha1 new file mode 100644 index 0000000000000..77b9917528382 --- /dev/null +++ b/plugins/repository-hdfs/licenses/slf4j-api-1.7.36.jar.sha1 @@ -0,0 +1 @@ +6c62681a2f655b49963a5983b8b0950a6120ae14 \ No newline at end of file diff --git a/plugins/repository-s3/licenses/commons-codec-1.13.jar.sha1 b/plugins/repository-s3/licenses/commons-codec-1.13.jar.sha1 deleted file mode 100644 index 66b72c414d63a..0000000000000 --- a/plugins/repository-s3/licenses/commons-codec-1.13.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -3f18e1aa31031d89db6f01ba05d501258ce69d2c \ No newline at end of file diff --git a/plugins/repository-s3/licenses/commons-codec-1.15.jar.sha1 b/plugins/repository-s3/licenses/commons-codec-1.15.jar.sha1 new file mode 100644 index 0000000000000..62d99837b87e1 --- /dev/null +++ b/plugins/repository-s3/licenses/commons-codec-1.15.jar.sha1 @@ -0,0 +1 @@ +49d94806b6e3dc933dacbd8acb0fdbab8ebd1e5d \ No newline at end of file diff --git a/plugins/repository-s3/licenses/httpcore-4.4.12.jar.sha1 b/plugins/repository-s3/licenses/httpcore-4.4.12.jar.sha1 deleted file mode 100644 index 3c046171b30da..0000000000000 --- a/plugins/repository-s3/licenses/httpcore-4.4.12.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -21ebaf6d532bc350ba95bd81938fa5f0e511c132 \ No newline at end of file diff --git a/plugins/repository-s3/licenses/httpcore-4.4.15.jar.sha1 b/plugins/repository-s3/licenses/httpcore-4.4.15.jar.sha1 new file mode 100644 index 0000000000000..42a03b5d7a376 --- /dev/null +++ b/plugins/repository-s3/licenses/httpcore-4.4.15.jar.sha1 @@ -0,0 +1 @@ +7f2e0c573eaa7a74bac2e89b359e1f73d92a0a1d \ No newline at end of file From 65f966ed71ff9bc0a53490ee801943869c0f536d Mon Sep 17 00:00:00 2001 From: Tianli Feng Date: Fri, 26 Aug 2022 15:42:31 -0700 Subject: [PATCH 014/187] Restore using the class ClusterInfoRequest and ClusterInfoRequestBuilder from package 'org.opensearch.action.support.master.info' for subclasses (#4307) * Restore using the class ClusterInfoRequest and ClusterInfoRequestBuilder from package 'org.opensearch.action.support.master.info' for subclasses Signed-off-by: Tianli Feng * Add changelog Signed-off-by: Tianli Feng Signed-off-by: Tianli Feng Co-authored-by: Andrew Ross --- CHANGELOG.md | 1 + .../admin/indices/get/GetIndexRequest.java | 2 +- .../indices/get/GetIndexRequestBuilder.java | 2 +- .../mapping/get/GetMappingsRequest.java | 2 +- .../get/GetMappingsRequestBuilder.java | 2 +- .../indices/get/GetIndexRequestTests.java | 21 +++++++++++++++++++ 6 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 server/src/test/java/org/opensearch/action/admin/indices/get/GetIndexRequestTests.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 26ff011609635..8132c1281e412 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### Fixed - `opensearch-service.bat start` and `opensearch-service.bat manager` failing to run ([#4289](https://github.com/opensearch-project/OpenSearch/pull/4289)) - PR reference to checkout code for changelog verifier ([#4296](https://github.com/opensearch-project/OpenSearch/pull/4296)) +- Restore using the class ClusterInfoRequest and ClusterInfoRequestBuilder from package 'org.opensearch.action.support.master.info' for subclasses ([#4307](https://github.com/opensearch-project/OpenSearch/pull/4307)) - Do not fail replica shard due to primary closure ([#4133](https://github.com/opensearch-project/OpenSearch/pull/4133)) ### Security diff --git a/server/src/main/java/org/opensearch/action/admin/indices/get/GetIndexRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/get/GetIndexRequest.java index ee0b204c77aa3..9a7fae9f84a98 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/get/GetIndexRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/get/GetIndexRequest.java @@ -33,7 +33,7 @@ package org.opensearch.action.admin.indices.get; import org.opensearch.action.ActionRequestValidationException; -import org.opensearch.action.support.clustermanager.info.ClusterInfoRequest; +import org.opensearch.action.support.master.info.ClusterInfoRequest; import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.util.ArrayUtils; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/get/GetIndexRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/get/GetIndexRequestBuilder.java index ed106c44ea36a..3019191e5570e 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/get/GetIndexRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/get/GetIndexRequestBuilder.java @@ -32,7 +32,7 @@ package org.opensearch.action.admin.indices.get; -import org.opensearch.action.support.clustermanager.info.ClusterInfoRequestBuilder; +import org.opensearch.action.support.master.info.ClusterInfoRequestBuilder; import org.opensearch.client.OpenSearchClient; /** diff --git a/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetMappingsRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetMappingsRequest.java index 1fd9323edd2f8..2c9bec8398b66 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetMappingsRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetMappingsRequest.java @@ -33,7 +33,7 @@ package org.opensearch.action.admin.indices.mapping.get; import org.opensearch.action.ActionRequestValidationException; -import org.opensearch.action.support.clustermanager.info.ClusterInfoRequest; +import org.opensearch.action.support.master.info.ClusterInfoRequest; import org.opensearch.common.io.stream.StreamInput; import java.io.IOException; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetMappingsRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetMappingsRequestBuilder.java index 0a6d7cac79133..85bf8c2ffd9c6 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetMappingsRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetMappingsRequestBuilder.java @@ -32,7 +32,7 @@ package org.opensearch.action.admin.indices.mapping.get; -import org.opensearch.action.support.clustermanager.info.ClusterInfoRequestBuilder; +import org.opensearch.action.support.master.info.ClusterInfoRequestBuilder; import org.opensearch.client.OpenSearchClient; /** diff --git a/server/src/test/java/org/opensearch/action/admin/indices/get/GetIndexRequestTests.java b/server/src/test/java/org/opensearch/action/admin/indices/get/GetIndexRequestTests.java new file mode 100644 index 0000000000000..f0d3db71c27b7 --- /dev/null +++ b/server/src/test/java/org/opensearch/action/admin/indices/get/GetIndexRequestTests.java @@ -0,0 +1,21 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.indices.get; + +import org.opensearch.action.support.master.info.ClusterInfoRequest; +import org.opensearch.test.OpenSearchTestCase; + +import static org.hamcrest.Matchers.is; + +public class GetIndexRequestTests extends OpenSearchTestCase { + public void testGetIndexRequestExtendsClusterInfoRequestOfDeprecatedClassPath() { + GetIndexRequest getIndexRequest = new GetIndexRequest().indices("test"); + assertThat(getIndexRequest instanceof ClusterInfoRequest, is(true)); + } +} From 7ea6e8865fa007471c187fe7b6cd7007059d6c69 Mon Sep 17 00:00:00 2001 From: Alex Burck Date: Mon, 29 Aug 2022 10:05:48 -0500 Subject: [PATCH 015/187] [BUG] Create logs directory before running OpenSearch on Windows (#4305) * [BUG] Create logs directory before running OpenSearch on Windows Signed-off-by: Alex Burck * update changlog pr link Signed-off-by: Alex Burck Signed-off-by: Alex Burck --- CHANGELOG.md | 1 + distribution/src/bin/opensearch-service.bat | 4 ++++ distribution/src/bin/opensearch.bat | 6 ++++++ 3 files changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8132c1281e412..360b47d05ff8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### Fixed - `opensearch-service.bat start` and `opensearch-service.bat manager` failing to run ([#4289](https://github.com/opensearch-project/OpenSearch/pull/4289)) - PR reference to checkout code for changelog verifier ([#4296](https://github.com/opensearch-project/OpenSearch/pull/4296)) +- `opensearch.bat` and `opensearch-service.bat install` failing to run, missing logs directory ([#4305](https://github.com/opensearch-project/OpenSearch/pull/4305)) - Restore using the class ClusterInfoRequest and ClusterInfoRequestBuilder from package 'org.opensearch.action.support.master.info' for subclasses ([#4307](https://github.com/opensearch-project/OpenSearch/pull/4307)) - Do not fail replica shard due to primary closure ([#4133](https://github.com/opensearch-project/OpenSearch/pull/4133)) diff --git a/distribution/src/bin/opensearch-service.bat b/distribution/src/bin/opensearch-service.bat index 8b91d806ef64f..a11dc8316e8b1 100644 --- a/distribution/src/bin/opensearch-service.bat +++ b/distribution/src/bin/opensearch-service.bat @@ -24,6 +24,10 @@ exit /B 1 set OPENSEARCH_VERSION=${project.version} if "%SERVICE_LOG_DIR%" == "" set SERVICE_LOG_DIR=%OPENSEARCH_HOME%\logs +rem The logs directory must exist for the service to start. +if not exist "%SERVICE_LOG_DIR%" ( + mkdir "%SERVICE_LOG_DIR%" +) if "x%1x" == "xx" goto displayUsage set SERVICE_CMD=%1 diff --git a/distribution/src/bin/opensearch.bat b/distribution/src/bin/opensearch.bat index 49a12aa5c968d..dda15124e1654 100644 --- a/distribution/src/bin/opensearch.bat +++ b/distribution/src/bin/opensearch.bat @@ -56,6 +56,12 @@ IF ERRORLEVEL 1 ( EXIT /B %ERRORLEVEL% ) +if "%SERVICE_LOG_DIR%" == "" set SERVICE_LOG_DIR=%OPENSEARCH_HOME%\logs +rem The logs directory must exist for the service to start. +if not exist "%SERVICE_LOG_DIR%" ( + mkdir "%SERVICE_LOG_DIR%" +) + SET KEYSTORE_PASSWORD= IF "%checkpassword%"=="Y" ( CALL "%~dp0opensearch-keystore.bat" has-passwd --silent From cd961f39bf57ae92b4486451ce2841b9682c2582 Mon Sep 17 00:00:00 2001 From: Sachin Kale Date: Mon, 29 Aug 2022 22:47:52 +0530 Subject: [PATCH 016/187] Use RemoteSegmentStoreDirectory instead of RemoteDirectory (#4240) * Use RemoteSegmentStoreDirectory instead of RemoteDirectory Signed-off-by: Sachin Kale --- CHANGELOG.md | 3 +- .../org/opensearch/index/IndexModule.java | 3 +- .../opensearch/index/shard/IndexShard.java | 5 +- .../shard/RemoteStoreRefreshListener.java | 176 +++++++++--- .../opensearch/index/shard/StoreRecovery.java | 7 +- .../index/store/RemoteIndexInput.java | 35 ++- .../store/RemoteSegmentStoreDirectory.java | 75 ++++- ...> RemoteSegmentStoreDirectoryFactory.java} | 22 +- .../opensearch/indices/IndicesService.java | 5 +- .../main/java/org/opensearch/node/Node.java | 6 +- .../opensearch/index/IndexModuleTests.java | 4 +- .../index/shard/IndexShardTests.java | 3 +- .../RemoteStoreRefreshListenerTests.java | 259 ++++++++++++------ .../index/store/RemoteIndexInputTests.java | 31 ++- ...oteSegmentStoreDirectoryFactoryTests.java} | 28 +- .../RemoteSegmentStoreDirectoryTests.java | 160 +++++++++-- .../snapshots/SnapshotResiliencyTests.java | 4 +- .../index/shard/IndexShardTestCase.java | 19 +- 18 files changed, 628 insertions(+), 217 deletions(-) rename server/src/main/java/org/opensearch/index/store/{RemoteDirectoryFactory.java => RemoteSegmentStoreDirectoryFactory.java} (58%) rename server/src/test/java/org/opensearch/index/store/{RemoteDirectoryFactoryTests.java => RemoteSegmentStoreDirectoryFactoryTests.java} (70%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 360b47d05ff8f..f11f407434e6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,8 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Github workflow for changelog verification ([#4085](https://github.com/opensearch-project/OpenSearch/pull/4085)) ### Changed - - Dependency updates (httpcore, mockito, slf4j, httpasyncclient, commons-codec) ([#4308](https://github.com/opensearch-project/OpenSearch/pull/4308)) +- Dependency updates (httpcore, mockito, slf4j, httpasyncclient, commons-codec) ([#4308](https://github.com/opensearch-project/OpenSearch/pull/4308)) +- Use RemoteSegmentStoreDirectory instead of RemoteDirectory ([#4240](https://github.com/opensearch-project/OpenSearch/pull/4240)) ### Deprecated diff --git a/server/src/main/java/org/opensearch/index/IndexModule.java b/server/src/main/java/org/opensearch/index/IndexModule.java index f8604caeab414..e52a2ba39ed52 100644 --- a/server/src/main/java/org/opensearch/index/IndexModule.java +++ b/server/src/main/java/org/opensearch/index/IndexModule.java @@ -70,7 +70,6 @@ import org.opensearch.index.shard.SearchOperationListener; import org.opensearch.index.similarity.SimilarityService; import org.opensearch.index.store.FsDirectoryFactory; -import org.opensearch.index.store.RemoteDirectoryFactory; import org.opensearch.indices.IndicesQueryCache; import org.opensearch.indices.breaker.CircuitBreakerService; import org.opensearch.indices.fielddata.cache.IndicesFieldDataCache; @@ -487,7 +486,7 @@ public IndexService newIndexService( NamedWriteableRegistry namedWriteableRegistry, BooleanSupplier idFieldDataEnabled, ValuesSourceRegistry valuesSourceRegistry, - RemoteDirectoryFactory remoteDirectoryFactory + IndexStorePlugin.RemoteDirectoryFactory remoteDirectoryFactory ) throws IOException { final IndexEventListener eventListener = freeze(); Function> readerWrapperFactory = indexReaderWrapper diff --git a/server/src/main/java/org/opensearch/index/shard/IndexShard.java b/server/src/main/java/org/opensearch/index/shard/IndexShard.java index 67a8e691fda0d..670af1f1c6fd9 100644 --- a/server/src/main/java/org/opensearch/index/shard/IndexShard.java +++ b/server/src/main/java/org/opensearch/index/shard/IndexShard.java @@ -48,8 +48,6 @@ import org.apache.lucene.search.Sort; import org.apache.lucene.search.UsageTrackingQueryCachingPolicy; import org.apache.lucene.store.AlreadyClosedException; -import org.apache.lucene.store.Directory; -import org.apache.lucene.store.FilterDirectory; import org.apache.lucene.util.SetOnce; import org.apache.lucene.util.ThreadInterruptedException; import org.opensearch.Assertions; @@ -3228,8 +3226,7 @@ private EngineConfig newEngineConfig(LongSupplier globalCheckpointSupplier) thro final List internalRefreshListener = new ArrayList<>(); internalRefreshListener.add(new RefreshMetricUpdater(refreshMetric)); if (isRemoteStoreEnabled()) { - Directory remoteDirectory = ((FilterDirectory) ((FilterDirectory) remoteStore.directory()).getDelegate()).getDelegate(); - internalRefreshListener.add(new RemoteStoreRefreshListener(store.directory(), remoteDirectory)); + internalRefreshListener.add(new RemoteStoreRefreshListener(this)); } if (this.checkpointPublisher != null && indexSettings.isSegRepEnabled() && shardRouting.primary()) { internalRefreshListener.add(new CheckpointRefreshListener(this, this.checkpointPublisher)); diff --git a/server/src/main/java/org/opensearch/index/shard/RemoteStoreRefreshListener.java b/server/src/main/java/org/opensearch/index/shard/RemoteStoreRefreshListener.java index 4b549ec485c0e..0d32e8d56e4d2 100644 --- a/server/src/main/java/org/opensearch/index/shard/RemoteStoreRefreshListener.java +++ b/server/src/main/java/org/opensearch/index/shard/RemoteStoreRefreshListener.java @@ -11,32 +11,54 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; +import org.apache.lucene.codecs.CodecUtil; +import org.apache.lucene.index.IndexFileNames; +import org.apache.lucene.index.SegmentInfos; import org.apache.lucene.search.ReferenceManager; import org.apache.lucene.store.Directory; +import org.apache.lucene.store.FilterDirectory; import org.apache.lucene.store.IOContext; +import org.apache.lucene.store.IndexInput; +import org.opensearch.common.concurrent.GatedCloseable; +import org.opensearch.index.engine.EngineException; +import org.opensearch.index.store.RemoteSegmentStoreDirectory; import java.io.IOException; -import java.nio.file.NoSuchFileException; -import java.util.Arrays; -import java.util.HashSet; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; /** * RefreshListener implementation to upload newly created segment files to the remote store + * + * @opensearch.internal */ -public class RemoteStoreRefreshListener implements ReferenceManager.RefreshListener { +public final class RemoteStoreRefreshListener implements ReferenceManager.RefreshListener { + // Visible for testing + static final Set EXCLUDE_FILES = Set.of("write.lock"); + // Visible for testing + static final int LAST_N_METADATA_FILES_TO_KEEP = 10; + private final IndexShard indexShard; private final Directory storeDirectory; - private final Directory remoteDirectory; - // ToDo: This can be a map with metadata of the uploaded file as value of the map (GitHub #3398) - private final Set filesUploadedToRemoteStore; + private final RemoteSegmentStoreDirectory remoteDirectory; + private final Map localSegmentChecksumMap; + private long primaryTerm; private static final Logger logger = LogManager.getLogger(RemoteStoreRefreshListener.class); - public RemoteStoreRefreshListener(Directory storeDirectory, Directory remoteDirectory) throws IOException { - this.storeDirectory = storeDirectory; - this.remoteDirectory = remoteDirectory; - // ToDo: Handle failures in reading list of files (GitHub #3397) - this.filesUploadedToRemoteStore = new HashSet<>(Arrays.asList(remoteDirectory.listAll())); + public RemoteStoreRefreshListener(IndexShard indexShard) { + this.indexShard = indexShard; + this.storeDirectory = indexShard.store().directory(); + this.remoteDirectory = (RemoteSegmentStoreDirectory) ((FilterDirectory) ((FilterDirectory) indexShard.remoteStore().directory()) + .getDelegate()).getDelegate(); + this.primaryTerm = indexShard.getOperationPrimaryTerm(); + localSegmentChecksumMap = new HashMap<>(); } @Override @@ -46,42 +68,112 @@ public void beforeRefresh() throws IOException { /** * Upload new segment files created as part of the last refresh to the remote segment store. - * The method also deletes segment files from remote store which are not part of local filesystem. + * This method also uploads remote_segments_metadata file which contains metadata of each segment file uploaded. * @param didRefresh true if the refresh opened a new reference - * @throws IOException in case of I/O error in reading list of local files */ @Override - public void afterRefresh(boolean didRefresh) throws IOException { - if (didRefresh) { - Set localFiles = Set.of(storeDirectory.listAll()); - localFiles.stream().filter(file -> !filesUploadedToRemoteStore.contains(file)).forEach(file -> { - try { - remoteDirectory.copyFrom(storeDirectory, file, file, IOContext.DEFAULT); - filesUploadedToRemoteStore.add(file); - } catch (NoSuchFileException e) { - logger.info( - () -> new ParameterizedMessage("The file {} does not exist anymore. It can happen in case of temp files", file), - e - ); - } catch (IOException e) { - // ToDO: Handle transient and permanent un-availability of the remote store (GitHub #3397) - logger.warn(() -> new ParameterizedMessage("Exception while uploading file {} to the remote segment store", file), e); - } - }); + public void afterRefresh(boolean didRefresh) { + synchronized (this) { + try { + if (indexShard.shardRouting.primary()) { + if (this.primaryTerm != indexShard.getOperationPrimaryTerm()) { + this.primaryTerm = indexShard.getOperationPrimaryTerm(); + this.remoteDirectory.init(); + } + try { + String lastCommittedLocalSegmentFileName = SegmentInfos.getLastCommitSegmentsFileName(storeDirectory); + if (!remoteDirectory.containsFile( + lastCommittedLocalSegmentFileName, + getChecksumOfLocalFile(lastCommittedLocalSegmentFileName) + )) { + deleteStaleCommits(); + } + try (GatedCloseable segmentInfosGatedCloseable = indexShard.getSegmentInfosSnapshot()) { + SegmentInfos segmentInfos = segmentInfosGatedCloseable.get(); + Collection refreshedLocalFiles = segmentInfos.files(true); + + List segmentInfosFiles = refreshedLocalFiles.stream() + .filter(file -> file.startsWith(IndexFileNames.SEGMENTS)) + .collect(Collectors.toList()); + Optional latestSegmentInfos = segmentInfosFiles.stream() + .max(Comparator.comparingLong(IndexFileNames::parseGeneration)); - Set remoteFilesToBeDeleted = new HashSet<>(); - // ToDo: Instead of deleting files in sync, mark them and delete in async/periodic flow (GitHub #3142) - filesUploadedToRemoteStore.stream().filter(file -> !localFiles.contains(file)).forEach(file -> { - try { - remoteDirectory.deleteFile(file); - remoteFilesToBeDeleted.add(file); - } catch (IOException e) { - // ToDO: Handle transient and permanent un-availability of the remote store (GitHub #3397) - logger.warn(() -> new ParameterizedMessage("Exception while deleting file {} from the remote segment store", file), e); + if (latestSegmentInfos.isPresent()) { + refreshedLocalFiles.addAll(SegmentInfos.readCommit(storeDirectory, latestSegmentInfos.get()).files(true)); + segmentInfosFiles.stream() + .filter(file -> !file.equals(latestSegmentInfos.get())) + .forEach(refreshedLocalFiles::remove); + + boolean uploadStatus = uploadNewSegments(refreshedLocalFiles); + if (uploadStatus) { + remoteDirectory.uploadMetadata( + refreshedLocalFiles, + storeDirectory, + indexShard.getOperationPrimaryTerm(), + segmentInfos.getGeneration() + ); + localSegmentChecksumMap.keySet() + .stream() + .filter(file -> !refreshedLocalFiles.contains(file)) + .collect(Collectors.toSet()) + .forEach(localSegmentChecksumMap::remove); + } + } + } catch (EngineException e) { + logger.warn("Exception while reading SegmentInfosSnapshot", e); + } + } catch (IOException e) { + // We don't want to fail refresh if upload of new segments fails. The missed segments will be re-tried + // in the next refresh. This should not affect durability of the indexed data after remote trans-log integration. + logger.warn("Exception while uploading new segments to the remote segment store", e); + } } - }); + } catch (Throwable t) { + logger.error("Exception in RemoteStoreRefreshListener.afterRefresh()", t); + } + } + } + + // Visible for testing + boolean uploadNewSegments(Collection localFiles) throws IOException { + AtomicBoolean uploadSuccess = new AtomicBoolean(true); + localFiles.stream().filter(file -> !EXCLUDE_FILES.contains(file)).filter(file -> { + try { + return !remoteDirectory.containsFile(file, getChecksumOfLocalFile(file)); + } catch (IOException e) { + logger.info( + "Exception while reading checksum of local segment file: {}, ignoring the exception and re-uploading the file", + file + ); + return true; + } + }).forEach(file -> { + try { + remoteDirectory.copyFrom(storeDirectory, file, file, IOContext.DEFAULT); + } catch (IOException e) { + uploadSuccess.set(false); + // ToDO: Handle transient and permanent un-availability of the remote store (GitHub #3397) + logger.warn(() -> new ParameterizedMessage("Exception while uploading file {} to the remote segment store", file), e); + } + }); + return uploadSuccess.get(); + } + + private String getChecksumOfLocalFile(String file) throws IOException { + if (!localSegmentChecksumMap.containsKey(file)) { + try (IndexInput indexInput = storeDirectory.openInput(file, IOContext.DEFAULT)) { + String checksum = Long.toString(CodecUtil.retrieveChecksum(indexInput)); + localSegmentChecksumMap.put(file, checksum); + } + } + return localSegmentChecksumMap.get(file); + } - remoteFilesToBeDeleted.forEach(filesUploadedToRemoteStore::remove); + private void deleteStaleCommits() { + try { + remoteDirectory.deleteStaleSegments(LAST_N_METADATA_FILES_TO_KEEP); + } catch (IOException e) { + logger.info("Exception while deleting stale commits from remote segment store, will retry delete post next commit", e); } } } diff --git a/server/src/main/java/org/opensearch/index/shard/StoreRecovery.java b/server/src/main/java/org/opensearch/index/shard/StoreRecovery.java index 1190e8e6ab3d2..06916c4cc87fe 100644 --- a/server/src/main/java/org/opensearch/index/shard/StoreRecovery.java +++ b/server/src/main/java/org/opensearch/index/shard/StoreRecovery.java @@ -449,7 +449,12 @@ private void recoverFromRemoteStore(IndexShard indexShard) throws IndexShardReco } indexShard.preRecovery(); indexShard.prepareForIndexRecovery(); - final Directory remoteDirectory = remoteStore.directory(); + assert remoteStore.directory() instanceof FilterDirectory : "Store.directory is not an instance of FilterDirectory"; + FilterDirectory remoteStoreDirectory = (FilterDirectory) remoteStore.directory(); + assert remoteStoreDirectory.getDelegate() instanceof FilterDirectory + : "Store.directory is not enclosing an instance of FilterDirectory"; + FilterDirectory byteSizeCachingStoreDirectory = (FilterDirectory) remoteStoreDirectory.getDelegate(); + final Directory remoteDirectory = byteSizeCachingStoreDirectory.getDelegate(); final Store store = indexShard.store(); final Directory storeDirectory = store.directory(); store.incRef(); diff --git a/server/src/main/java/org/opensearch/index/store/RemoteIndexInput.java b/server/src/main/java/org/opensearch/index/store/RemoteIndexInput.java index 8f8d5dd5418ae..2c809563ca961 100644 --- a/server/src/main/java/org/opensearch/index/store/RemoteIndexInput.java +++ b/server/src/main/java/org/opensearch/index/store/RemoteIndexInput.java @@ -27,27 +27,37 @@ public class RemoteIndexInput extends IndexInput { private final InputStream inputStream; private final long size; + private long filePointer; public RemoteIndexInput(String name, InputStream inputStream, long size) { super(name); this.inputStream = inputStream; this.size = size; + this.filePointer = 0; } @Override public byte readByte() throws IOException { byte[] buffer = new byte[1]; - inputStream.read(buffer); + int numberOfBytesRead = inputStream.read(buffer); + if (numberOfBytesRead != -1) { + filePointer += numberOfBytesRead; + } return buffer[0]; } @Override public void readBytes(byte[] b, int offset, int len) throws IOException { int bytesRead = inputStream.read(b, offset, len); - while (bytesRead > 0 && bytesRead < len) { - len -= bytesRead; - offset += bytesRead; - bytesRead = inputStream.read(b, offset, len); + if (bytesRead == len) { + filePointer += bytesRead; + } else { + while (bytesRead > 0 && bytesRead < len) { + filePointer += bytesRead; + len -= bytesRead; + offset += bytesRead; + bytesRead = inputStream.read(b, offset, len); + } } } @@ -61,11 +71,6 @@ public long length() { return size; } - @Override - public void seek(long pos) throws IOException { - inputStream.skip(pos); - } - /** * Guaranteed to throw an exception and leave the RemoteIndexInput unmodified. * This method is not implemented as it is not used for the file transfer to/from the remote store. @@ -73,10 +78,18 @@ public void seek(long pos) throws IOException { * @throws UnsupportedOperationException always */ @Override - public long getFilePointer() { + public void seek(long pos) throws IOException { throw new UnsupportedOperationException(); } + /** + * Returns the current position in this file in terms of number of bytes read so far. + */ + @Override + public long getFilePointer() { + return filePointer; + } + /** * Guaranteed to throw an exception and leave the RemoteIndexInput unmodified. * This method is not implemented as it is not used for the file transfer to/from the remote store. diff --git a/server/src/main/java/org/opensearch/index/store/RemoteSegmentStoreDirectory.java b/server/src/main/java/org/opensearch/index/store/RemoteSegmentStoreDirectory.java index d7d6b29d08bfc..505ad6fafd550 100644 --- a/server/src/main/java/org/opensearch/index/store/RemoteSegmentStoreDirectory.java +++ b/server/src/main/java/org/opensearch/index/store/RemoteSegmentStoreDirectory.java @@ -24,9 +24,13 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashMap; +import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; /** @@ -132,8 +136,9 @@ private Map readMetadataFile(String metadataFil /** * Metadata of a segment that is uploaded to remote segment store. */ - static class UploadedSegmentMetadata { - private static final String SEPARATOR = "::"; + public static class UploadedSegmentMetadata { + // Visible for testing + static final String SEPARATOR = "::"; private final String originalFilename; private final String uploadedFilename; private final String checksum; @@ -366,7 +371,69 @@ private String getLocalSegmentFilename(String remoteFilename) { } // Visible for testing - Map getSegmentsUploadedToRemoteStore() { - return this.segmentsUploadedToRemoteStore; + public Map getSegmentsUploadedToRemoteStore() { + return Collections.unmodifiableMap(this.segmentsUploadedToRemoteStore); + } + + /** + * Delete stale segment and metadata files + * One metadata file is kept per commit (refresh updates the same file). To read segments uploaded to remote store, + * we just need to read the latest metadata file. All the stale metadata files can be safely deleted. + * @param lastNMetadataFilesToKeep number of metadata files to keep + * @throws IOException in case of I/O error while reading from / writing to remote segment store + */ + public void deleteStaleSegments(int lastNMetadataFilesToKeep) throws IOException { + Collection metadataFiles = remoteMetadataDirectory.listFilesByPrefix(MetadataFilenameUtils.METADATA_PREFIX); + List sortedMetadataFileList = metadataFiles.stream().sorted(METADATA_FILENAME_COMPARATOR).collect(Collectors.toList()); + if (sortedMetadataFileList.size() <= lastNMetadataFilesToKeep) { + logger.info( + "Number of commits in remote segment store={}, lastNMetadataFilesToKeep={}", + sortedMetadataFileList.size(), + lastNMetadataFilesToKeep + ); + return; + } + List latestNMetadataFiles = sortedMetadataFileList.subList( + sortedMetadataFileList.size() - lastNMetadataFilesToKeep, + sortedMetadataFileList.size() + ); + Map activeSegmentFilesMetadataMap = new HashMap<>(); + Set activeSegmentRemoteFilenames = new HashSet<>(); + for (String metadataFile : latestNMetadataFiles) { + Map segmentMetadataMap = readMetadataFile(metadataFile); + activeSegmentFilesMetadataMap.putAll(segmentMetadataMap); + activeSegmentRemoteFilenames.addAll( + segmentMetadataMap.values().stream().map(metadata -> metadata.uploadedFilename).collect(Collectors.toSet()) + ); + } + for (String metadataFile : sortedMetadataFileList.subList(0, sortedMetadataFileList.size() - lastNMetadataFilesToKeep)) { + Map staleSegmentFilesMetadataMap = readMetadataFile(metadataFile); + Set staleSegmentRemoteFilenames = staleSegmentFilesMetadataMap.values() + .stream() + .map(metadata -> metadata.uploadedFilename) + .collect(Collectors.toSet()); + AtomicBoolean deletionSuccessful = new AtomicBoolean(true); + staleSegmentRemoteFilenames.stream().filter(file -> !activeSegmentRemoteFilenames.contains(file)).forEach(file -> { + try { + remoteDataDirectory.deleteFile(file); + if (!activeSegmentFilesMetadataMap.containsKey(getLocalSegmentFilename(file))) { + segmentsUploadedToRemoteStore.remove(getLocalSegmentFilename(file)); + } + } catch (NoSuchFileException e) { + logger.info("Segment file {} corresponding to metadata file {} does not exist in remote", file, metadataFile); + } catch (IOException e) { + deletionSuccessful.set(false); + logger.info( + "Exception while deleting segment file {} corresponding to metadata file {}. Deletion will be re-tried", + file, + metadataFile + ); + } + }); + if (deletionSuccessful.get()) { + logger.info("Deleting stale metadata file {} from remote segment store", metadataFile); + remoteMetadataDirectory.deleteFile(metadataFile); + } + } } } diff --git a/server/src/main/java/org/opensearch/index/store/RemoteDirectoryFactory.java b/server/src/main/java/org/opensearch/index/store/RemoteSegmentStoreDirectoryFactory.java similarity index 58% rename from server/src/main/java/org/opensearch/index/store/RemoteDirectoryFactory.java rename to server/src/main/java/org/opensearch/index/store/RemoteSegmentStoreDirectoryFactory.java index 62f398cdad207..e77eb52bd3891 100644 --- a/server/src/main/java/org/opensearch/index/store/RemoteDirectoryFactory.java +++ b/server/src/main/java/org/opensearch/index/store/RemoteSegmentStoreDirectoryFactory.java @@ -27,11 +27,11 @@ * * @opensearch.internal */ -public class RemoteDirectoryFactory implements IndexStorePlugin.RemoteDirectoryFactory { +public class RemoteSegmentStoreDirectoryFactory implements IndexStorePlugin.RemoteDirectoryFactory { private final Supplier repositoriesService; - public RemoteDirectoryFactory(Supplier repositoriesService) { + public RemoteSegmentStoreDirectoryFactory(Supplier repositoriesService) { this.repositoriesService = repositoriesService; } @@ -39,13 +39,23 @@ public RemoteDirectoryFactory(Supplier repositoriesService) public Directory newDirectory(String repositoryName, IndexSettings indexSettings, ShardPath path) throws IOException { try (Repository repository = repositoriesService.get().repository(repositoryName)) { assert repository instanceof BlobStoreRepository : "repository should be instance of BlobStoreRepository"; - BlobPath blobPath = new BlobPath(); - blobPath = blobPath.add(indexSettings.getIndex().getName()).add(String.valueOf(path.getShardId().getId())); - BlobContainer blobContainer = ((BlobStoreRepository) repository).blobStore().blobContainer(blobPath); - return new RemoteDirectory(blobContainer); + BlobPath commonBlobPath = ((BlobStoreRepository) repository).basePath(); + commonBlobPath = commonBlobPath.add(indexSettings.getIndex().getUUID()) + .add(String.valueOf(path.getShardId().getId())) + .add("segments"); + + RemoteDirectory dataDirectory = createRemoteDirectory(repository, commonBlobPath, "data"); + RemoteDirectory metadataDirectory = createRemoteDirectory(repository, commonBlobPath, "metadata"); + + return new RemoteSegmentStoreDirectory(dataDirectory, metadataDirectory); } catch (RepositoryMissingException e) { throw new IllegalArgumentException("Repository should be created before creating index with remote_store enabled setting", e); } } + private RemoteDirectory createRemoteDirectory(Repository repository, BlobPath commonBlobPath, String extention) { + BlobPath extendedPath = commonBlobPath.add(extention); + BlobContainer dataBlobContainer = ((BlobStoreRepository) repository).blobStore().blobContainer(extendedPath); + return new RemoteDirectory(dataBlobContainer); + } } diff --git a/server/src/main/java/org/opensearch/indices/IndicesService.java b/server/src/main/java/org/opensearch/indices/IndicesService.java index fdb609ba7bbff..6808803ee0988 100644 --- a/server/src/main/java/org/opensearch/indices/IndicesService.java +++ b/server/src/main/java/org/opensearch/indices/IndicesService.java @@ -132,7 +132,6 @@ import org.opensearch.index.shard.IndexingOperationListener; import org.opensearch.index.shard.IndexingStats; import org.opensearch.index.shard.ShardId; -import org.opensearch.index.store.RemoteDirectoryFactory; import org.opensearch.indices.breaker.CircuitBreakerService; import org.opensearch.indices.cluster.IndicesClusterStateService; import org.opensearch.indices.fielddata.cache.IndicesFieldDataCache; @@ -266,7 +265,7 @@ public class IndicesService extends AbstractLifecycleComponent private final Set danglingIndicesToWrite = Sets.newConcurrentHashSet(); private final boolean nodeWriteDanglingIndicesInfo; private final ValuesSourceRegistry valuesSourceRegistry; - private final RemoteDirectoryFactory remoteDirectoryFactory; + private final IndexStorePlugin.RemoteDirectoryFactory remoteDirectoryFactory; @Override protected void doStart() { @@ -295,7 +294,7 @@ public IndicesService( Map directoryFactories, ValuesSourceRegistry valuesSourceRegistry, Map recoveryStateFactories, - RemoteDirectoryFactory remoteDirectoryFactory + IndexStorePlugin.RemoteDirectoryFactory remoteDirectoryFactory ) { this.settings = settings; this.threadPool = threadPool; diff --git a/server/src/main/java/org/opensearch/node/Node.java b/server/src/main/java/org/opensearch/node/Node.java index d3f0912cab638..3f4eadc52fd2a 100644 --- a/server/src/main/java/org/opensearch/node/Node.java +++ b/server/src/main/java/org/opensearch/node/Node.java @@ -39,12 +39,12 @@ import org.opensearch.common.util.FeatureFlags; import org.opensearch.cluster.routing.allocation.AwarenessReplicaBalance; import org.opensearch.index.IndexingPressureService; -import org.opensearch.index.store.RemoteDirectoryFactory; import org.opensearch.indices.replication.SegmentReplicationSourceFactory; import org.opensearch.indices.replication.SegmentReplicationTargetService; import org.opensearch.indices.replication.SegmentReplicationSourceService; import org.opensearch.tasks.TaskResourceTrackingService; import org.opensearch.threadpool.RunnableTaskExecutionListener; +import org.opensearch.index.store.RemoteSegmentStoreDirectoryFactory; import org.opensearch.watcher.ResourceWatcherService; import org.opensearch.Assertions; import org.opensearch.Build; @@ -629,7 +629,9 @@ protected Node( rerouteServiceReference.set(rerouteService); clusterService.setRerouteService(rerouteService); - final RemoteDirectoryFactory remoteDirectoryFactory = new RemoteDirectoryFactory(repositoriesServiceReference::get); + final IndexStorePlugin.RemoteDirectoryFactory remoteDirectoryFactory = new RemoteSegmentStoreDirectoryFactory( + repositoriesServiceReference::get + ); final IndicesService indicesService = new IndicesService( settings, diff --git a/server/src/test/java/org/opensearch/index/IndexModuleTests.java b/server/src/test/java/org/opensearch/index/IndexModuleTests.java index 45d93a5a12847..6bfdd9ae16773 100644 --- a/server/src/test/java/org/opensearch/index/IndexModuleTests.java +++ b/server/src/test/java/org/opensearch/index/IndexModuleTests.java @@ -89,7 +89,7 @@ import org.opensearch.index.similarity.NonNegativeScoresSimilarity; import org.opensearch.index.similarity.SimilarityService; import org.opensearch.index.store.FsDirectoryFactory; -import org.opensearch.index.store.RemoteDirectoryFactory; +import org.opensearch.index.store.RemoteSegmentStoreDirectoryFactory; import org.opensearch.indices.IndicesModule; import org.opensearch.indices.IndicesQueryCache; import org.opensearch.indices.analysis.AnalysisModule; @@ -234,7 +234,7 @@ private IndexService newIndexService(IndexModule module) throws IOException { writableRegistry(), () -> false, null, - new RemoteDirectoryFactory(() -> repositoriesService) + new RemoteSegmentStoreDirectoryFactory(() -> repositoriesService) ); } diff --git a/server/src/test/java/org/opensearch/index/shard/IndexShardTests.java b/server/src/test/java/org/opensearch/index/shard/IndexShardTests.java index 8c00ab97a46ea..662afa80f65fc 100644 --- a/server/src/test/java/org/opensearch/index/shard/IndexShardTests.java +++ b/server/src/test/java/org/opensearch/index/shard/IndexShardTests.java @@ -2689,8 +2689,9 @@ public void testRestoreShardFromRemoteStore() throws IOException { storeDirectory.deleteFile(file); } + assertEquals(0, storeDirectory.listAll().length); + Directory remoteDirectory = ((FilterDirectory) ((FilterDirectory) target.remoteStore().directory()).getDelegate()).getDelegate(); - ((BaseDirectoryWrapper) remoteDirectory).setCheckIndexOnClose(false); // extra0 file is added as a part of https://lucene.apache.org/core/7_2_1/test-framework/org/apache/lucene/mockfile/ExtrasFS.html // Safe to remove without impacting the test diff --git a/server/src/test/java/org/opensearch/index/shard/RemoteStoreRefreshListenerTests.java b/server/src/test/java/org/opensearch/index/shard/RemoteStoreRefreshListenerTests.java index af92d821a9043..6b05d67836272 100644 --- a/server/src/test/java/org/opensearch/index/shard/RemoteStoreRefreshListenerTests.java +++ b/server/src/test/java/org/opensearch/index/shard/RemoteStoreRefreshListenerTests.java @@ -8,132 +8,209 @@ package org.opensearch.index.shard; +import org.apache.lucene.index.SegmentInfos; import org.apache.lucene.store.Directory; -import org.apache.lucene.store.IOContext; -import org.opensearch.test.OpenSearchTestCase; +import org.apache.lucene.store.FilterDirectory; +import org.apache.lucene.tests.store.BaseDirectoryWrapper; +import org.junit.After; +import org.opensearch.action.ActionListener; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.cluster.routing.IndexShardRoutingTable; +import org.opensearch.cluster.routing.ShardRouting; +import org.opensearch.common.concurrent.GatedCloseable; +import org.opensearch.common.lease.Releasable; +import org.opensearch.common.settings.Settings; +import org.opensearch.index.engine.InternalEngineFactory; +import org.opensearch.index.store.RemoteSegmentStoreDirectory; +import org.opensearch.index.store.Store; +import org.opensearch.threadpool.ThreadPool; import java.io.IOException; -import java.nio.file.NoSuchFileException; +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.CountDownLatch; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.doThrow; +public class RemoteStoreRefreshListenerTests extends IndexShardTestCase { + private IndexShard indexShard; + private RemoteStoreRefreshListener remoteStoreRefreshListener; -public class RemoteStoreRefreshListenerTests extends OpenSearchTestCase { - private Directory storeDirectory; - private Directory remoteDirectory; + public void setup(boolean primary, int numberOfDocs) throws IOException { + indexShard = newStartedShard( + primary, + Settings.builder().put(IndexMetadata.SETTING_REMOTE_STORE_ENABLED, true).build(), + new InternalEngineFactory() + ); - private RemoteStoreRefreshListener remoteStoreRefreshListener; + indexDocs(1, numberOfDocs); + indexShard.refresh("test"); - public void setup(String[] remoteFiles) throws IOException { - storeDirectory = mock(Directory.class); - remoteDirectory = mock(Directory.class); - when(remoteDirectory.listAll()).thenReturn(remoteFiles); - remoteStoreRefreshListener = new RemoteStoreRefreshListener(storeDirectory, remoteDirectory); + remoteStoreRefreshListener = new RemoteStoreRefreshListener(indexShard); } - public void testAfterRefreshFalse() throws IOException { - setup(new String[0]); - remoteStoreRefreshListener.afterRefresh(false); - verify(storeDirectory, times(0)).listAll(); + private void indexDocs(int startDocId, int numberOfDocs) throws IOException { + for (int i = startDocId; i < startDocId + numberOfDocs; i++) { + indexDoc(indexShard, "_doc", Integer.toString(i)); + } } - public void testAfterRefreshTrueNoLocalFiles() throws IOException { - setup(new String[0]); + @After + public void tearDown() throws Exception { + Directory storeDirectory = ((FilterDirectory) ((FilterDirectory) indexShard.store().directory()).getDelegate()).getDelegate(); + ((BaseDirectoryWrapper) storeDirectory).setCheckIndexOnClose(false); + closeShards(indexShard); + super.tearDown(); + } - when(storeDirectory.listAll()).thenReturn(new String[0]); + public void testAfterRefresh() throws IOException { + setup(true, 3); + assertDocs(indexShard, "1", "2", "3"); - remoteStoreRefreshListener.afterRefresh(true); - verify(storeDirectory).listAll(); - verify(remoteDirectory, times(0)).copyFrom(any(), any(), any(), any()); - verify(remoteDirectory, times(0)).deleteFile(any()); - } + try (Store remoteStore = indexShard.remoteStore()) { + RemoteSegmentStoreDirectory remoteSegmentStoreDirectory = + (RemoteSegmentStoreDirectory) ((FilterDirectory) ((FilterDirectory) remoteStore.directory()).getDelegate()).getDelegate(); - public void testAfterRefreshOnlyUploadFiles() throws IOException { - setup(new String[0]); + verifyUploadedSegments(remoteSegmentStoreDirectory); - String[] localFiles = new String[] { "segments_1", "0.si", "0.cfs", "0.cfe" }; - when(storeDirectory.listAll()).thenReturn(localFiles); + // This is to check if reading data from remote segment store works as well. + remoteSegmentStoreDirectory.init(); - remoteStoreRefreshListener.afterRefresh(true); - verify(storeDirectory).listAll(); - verify(remoteDirectory).copyFrom(storeDirectory, "segments_1", "segments_1", IOContext.DEFAULT); - verify(remoteDirectory).copyFrom(storeDirectory, "0.si", "0.si", IOContext.DEFAULT); - verify(remoteDirectory).copyFrom(storeDirectory, "0.cfs", "0.cfs", IOContext.DEFAULT); - verify(remoteDirectory).copyFrom(storeDirectory, "0.cfe", "0.cfe", IOContext.DEFAULT); - verify(remoteDirectory, times(0)).deleteFile(any()); + verifyUploadedSegments(remoteSegmentStoreDirectory); + } } - public void testAfterRefreshOnlyUploadAndDelete() throws IOException { - setup(new String[] { "0.si", "0.cfs" }); + public void testAfterCommit() throws IOException { + setup(true, 3); + assertDocs(indexShard, "1", "2", "3"); + flushShard(indexShard); - String[] localFiles = new String[] { "segments_1", "1.si", "1.cfs", "1.cfe" }; - when(storeDirectory.listAll()).thenReturn(localFiles); + try (Store remoteStore = indexShard.remoteStore()) { + RemoteSegmentStoreDirectory remoteSegmentStoreDirectory = + (RemoteSegmentStoreDirectory) ((FilterDirectory) ((FilterDirectory) remoteStore.directory()).getDelegate()).getDelegate(); - remoteStoreRefreshListener.afterRefresh(true); - verify(storeDirectory).listAll(); - verify(remoteDirectory).copyFrom(storeDirectory, "segments_1", "segments_1", IOContext.DEFAULT); - verify(remoteDirectory).copyFrom(storeDirectory, "1.si", "1.si", IOContext.DEFAULT); - verify(remoteDirectory).copyFrom(storeDirectory, "1.cfs", "1.cfs", IOContext.DEFAULT); - verify(remoteDirectory).copyFrom(storeDirectory, "1.cfe", "1.cfe", IOContext.DEFAULT); - verify(remoteDirectory).deleteFile("0.si"); - verify(remoteDirectory).deleteFile("0.cfs"); + verifyUploadedSegments(remoteSegmentStoreDirectory); + + // This is to check if reading data from remote segment store works as well. + remoteSegmentStoreDirectory.init(); + + verifyUploadedSegments(remoteSegmentStoreDirectory); + } } - public void testAfterRefreshOnlyDelete() throws IOException { - setup(new String[] { "0.si", "0.cfs" }); + public void testRefreshAfterCommit() throws IOException { + setup(true, 3); + assertDocs(indexShard, "1", "2", "3"); + flushShard(indexShard); - String[] localFiles = new String[] { "0.si" }; - when(storeDirectory.listAll()).thenReturn(localFiles); + indexDocs(4, 4); + indexShard.refresh("test"); - remoteStoreRefreshListener.afterRefresh(true); - verify(storeDirectory).listAll(); - verify(remoteDirectory, times(0)).copyFrom(any(), any(), any(), any()); - verify(remoteDirectory).deleteFile("0.cfs"); - } + indexDocs(8, 4); + indexShard.refresh("test"); - public void testAfterRefreshTempLocalFile() throws IOException { - setup(new String[0]); + try (Store remoteStore = indexShard.remoteStore()) { + RemoteSegmentStoreDirectory remoteSegmentStoreDirectory = + (RemoteSegmentStoreDirectory) ((FilterDirectory) ((FilterDirectory) remoteStore.directory()).getDelegate()).getDelegate(); - String[] localFiles = new String[] { "segments_1", "0.si", "0.cfs.tmp" }; - when(storeDirectory.listAll()).thenReturn(localFiles); - doThrow(new NoSuchFileException("0.cfs.tmp")).when(remoteDirectory) - .copyFrom(storeDirectory, "0.cfs.tmp", "0.cfs.tmp", IOContext.DEFAULT); + verifyUploadedSegments(remoteSegmentStoreDirectory); - remoteStoreRefreshListener.afterRefresh(true); - verify(storeDirectory).listAll(); - verify(remoteDirectory).copyFrom(storeDirectory, "segments_1", "segments_1", IOContext.DEFAULT); - verify(remoteDirectory).copyFrom(storeDirectory, "0.si", "0.si", IOContext.DEFAULT); - verify(remoteDirectory, times(0)).deleteFile(any()); + // This is to check if reading data from remote segment store works as well. + remoteSegmentStoreDirectory.init(); + + verifyUploadedSegments(remoteSegmentStoreDirectory); + } } - public void testAfterRefreshConsecutive() throws IOException { - setup(new String[0]); + public void testAfterMultipleCommits() throws IOException { + setup(true, 3); + assertDocs(indexShard, "1", "2", "3"); - String[] localFiles = new String[] { "segments_1", "0.si", "0.cfs", "0.cfe" }; - when(storeDirectory.listAll()).thenReturn(localFiles); - doThrow(new IOException("0.cfs")).when(remoteDirectory).copyFrom(storeDirectory, "0.cfs", "0.cfe", IOContext.DEFAULT); - doThrow(new IOException("0.cfe")).when(remoteDirectory).copyFrom(storeDirectory, "0.cfe", "0.cfe", IOContext.DEFAULT); + for (int i = 0; i < RemoteStoreRefreshListener.LAST_N_METADATA_FILES_TO_KEEP + 3; i++) { + indexDocs(4 * (i + 1), 4); + flushShard(indexShard); + } + try (Store remoteStore = indexShard.remoteStore()) { + RemoteSegmentStoreDirectory remoteSegmentStoreDirectory = + (RemoteSegmentStoreDirectory) ((FilterDirectory) ((FilterDirectory) remoteStore.directory()).getDelegate()).getDelegate(); + + verifyUploadedSegments(remoteSegmentStoreDirectory); + + // This is to check if reading data from remote segment store works as well. + remoteSegmentStoreDirectory.init(); + + verifyUploadedSegments(remoteSegmentStoreDirectory); + } + } + + public void testReplica() throws IOException { + setup(false, 3); remoteStoreRefreshListener.afterRefresh(true); - verify(storeDirectory).listAll(); - verify(remoteDirectory).copyFrom(storeDirectory, "segments_1", "segments_1", IOContext.DEFAULT); - verify(remoteDirectory).copyFrom(storeDirectory, "0.si", "0.si", IOContext.DEFAULT); - verify(remoteDirectory).copyFrom(storeDirectory, "0.cfs", "0.cfs", IOContext.DEFAULT); - verify(remoteDirectory).copyFrom(storeDirectory, "0.cfe", "0.cfe", IOContext.DEFAULT); - verify(remoteDirectory, times(0)).deleteFile(any()); - String[] localFilesSecondRefresh = new String[] { "segments_1", "0.cfs", "1.cfs", "1.cfe" }; - when(storeDirectory.listAll()).thenReturn(localFilesSecondRefresh); + try (Store remoteStore = indexShard.remoteStore()) { + RemoteSegmentStoreDirectory remoteSegmentStoreDirectory = + (RemoteSegmentStoreDirectory) ((FilterDirectory) ((FilterDirectory) remoteStore.directory()).getDelegate()).getDelegate(); + + assertEquals(0, remoteSegmentStoreDirectory.getSegmentsUploadedToRemoteStore().size()); + } + } + public void testReplicaPromotion() throws IOException, InterruptedException { + setup(false, 3); remoteStoreRefreshListener.afterRefresh(true); - verify(remoteDirectory).copyFrom(storeDirectory, "0.cfs", "0.cfs", IOContext.DEFAULT); - verify(remoteDirectory).copyFrom(storeDirectory, "1.cfs", "1.cfs", IOContext.DEFAULT); - verify(remoteDirectory).copyFrom(storeDirectory, "1.cfe", "1.cfe", IOContext.DEFAULT); - verify(remoteDirectory).deleteFile("0.si"); + RemoteSegmentStoreDirectory remoteSegmentStoreDirectory = + (RemoteSegmentStoreDirectory) ((FilterDirectory) ((FilterDirectory) indexShard.remoteStore().directory()).getDelegate()) + .getDelegate(); + + assertEquals(0, remoteSegmentStoreDirectory.getSegmentsUploadedToRemoteStore().size()); + + final ShardRouting replicaRouting = indexShard.routingEntry(); + promoteReplica( + indexShard, + Collections.singleton(replicaRouting.allocationId().getId()), + new IndexShardRoutingTable.Builder(replicaRouting.shardId()).addShard(replicaRouting).build() + ); + + // The following logic is referenced from IndexShardTests.testPrimaryFillsSeqNoGapsOnPromotion + // ToDo: Add wait logic as part of promoteReplica() + final CountDownLatch latch = new CountDownLatch(1); + indexShard.acquirePrimaryOperationPermit(new ActionListener<>() { + @Override + public void onResponse(Releasable releasable) { + releasable.close(); + latch.countDown(); + } + + @Override + public void onFailure(Exception e) { + throw new AssertionError(e); + } + }, ThreadPool.Names.GENERIC, ""); + + latch.await(); + + indexDocs(4, 4); + indexShard.refresh("test"); + remoteStoreRefreshListener.afterRefresh(true); + + verifyUploadedSegments(remoteSegmentStoreDirectory); + + // This is to check if reading data from remote segment store works as well. + remoteSegmentStoreDirectory.init(); + + verifyUploadedSegments(remoteSegmentStoreDirectory); + } + + private void verifyUploadedSegments(RemoteSegmentStoreDirectory remoteSegmentStoreDirectory) throws IOException { + Map uploadedSegments = remoteSegmentStoreDirectory + .getSegmentsUploadedToRemoteStore(); + try (GatedCloseable segmentInfosGatedCloseable = indexShard.getSegmentInfosSnapshot()) { + SegmentInfos segmentInfos = segmentInfosGatedCloseable.get(); + for (String file : segmentInfos.files(true)) { + if (!RemoteStoreRefreshListener.EXCLUDE_FILES.contains(file)) { + assertTrue(uploadedSegments.containsKey(file)); + } + } + } } } diff --git a/server/src/test/java/org/opensearch/index/store/RemoteIndexInputTests.java b/server/src/test/java/org/opensearch/index/store/RemoteIndexInputTests.java index 273d3c7e37c56..cd35349e33b59 100644 --- a/server/src/test/java/org/opensearch/index/store/RemoteIndexInputTests.java +++ b/server/src/test/java/org/opensearch/index/store/RemoteIndexInputTests.java @@ -44,6 +44,7 @@ public void testReadByte() throws IOException { when(inputStream.read()).thenReturn(10); assertEquals(10, remoteIndexInput.readByte()); + assertEquals(1, remoteIndexInput.getFilePointer()); verify(inputStream).read(any()); } @@ -52,13 +53,19 @@ public void testReadByteIOException() throws IOException { when(inputStream.read(any())).thenThrow(new IOException("Error reading")); assertThrows(IOException.class, () -> remoteIndexInput.readByte()); + assertEquals(0, remoteIndexInput.getFilePointer()); } public void testReadBytes() throws IOException { - byte[] buffer = new byte[10]; - remoteIndexInput.readBytes(buffer, 10, 20); + byte[] buffer = new byte[20]; + when(inputStream.read(eq(buffer), anyInt(), anyInt())).thenReturn(10).thenReturn(3).thenReturn(6).thenReturn(-1); + remoteIndexInput.readBytes(buffer, 0, 20); - verify(inputStream).read(buffer, 10, 20); + verify(inputStream).read(buffer, 0, 20); + verify(inputStream).read(buffer, 10, 10); + verify(inputStream).read(buffer, 13, 7); + verify(inputStream).read(buffer, 19, 1); + assertEquals(19, remoteIndexInput.getFilePointer()); } public void testReadBytesMultipleIterations() throws IOException { @@ -95,20 +102,14 @@ public void testLength() { assertEquals(FILESIZE, remoteIndexInput.length()); } - public void testSeek() throws IOException { - remoteIndexInput.seek(10); - - verify(inputStream).skip(10); - } - - public void testSeekIOException() throws IOException { - when(inputStream.skip(10)).thenThrow(new IOException("Error reading")); - - assertThrows(IOException.class, () -> remoteIndexInput.seek(10)); + public void testSeek() { + assertThrows(UnsupportedOperationException.class, () -> remoteIndexInput.seek(100L)); } - public void testGetFilePointer() { - assertThrows(UnsupportedOperationException.class, () -> remoteIndexInput.getFilePointer()); + public void testGetFilePointer() throws IOException { + when(inputStream.read(any(), eq(0), eq(8))).thenReturn(8); + remoteIndexInput.readBytes(new byte[8], 0, 8); + assertEquals(8, remoteIndexInput.getFilePointer()); } public void testSlice() { diff --git a/server/src/test/java/org/opensearch/index/store/RemoteDirectoryFactoryTests.java b/server/src/test/java/org/opensearch/index/store/RemoteSegmentStoreDirectoryFactoryTests.java similarity index 70% rename from server/src/test/java/org/opensearch/index/store/RemoteDirectoryFactoryTests.java rename to server/src/test/java/org/opensearch/index/store/RemoteSegmentStoreDirectoryFactoryTests.java index e8357d2c184bf..0105d0dc309c2 100644 --- a/server/src/test/java/org/opensearch/index/store/RemoteDirectoryFactoryTests.java +++ b/server/src/test/java/org/opensearch/index/store/RemoteSegmentStoreDirectoryFactoryTests.java @@ -11,6 +11,7 @@ import org.apache.lucene.store.Directory; import org.junit.Before; import org.mockito.ArgumentCaptor; +import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.blobstore.BlobContainer; import org.opensearch.common.blobstore.BlobPath; import org.opensearch.common.blobstore.BlobStore; @@ -27,29 +28,31 @@ import java.io.IOException; import java.nio.file.Path; import java.util.Collections; +import java.util.List; import java.util.function.Supplier; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.times; -public class RemoteDirectoryFactoryTests extends OpenSearchTestCase { +public class RemoteSegmentStoreDirectoryFactoryTests extends OpenSearchTestCase { private Supplier repositoriesServiceSupplier; private RepositoriesService repositoriesService; - private RemoteDirectoryFactory remoteDirectoryFactory; + private RemoteSegmentStoreDirectoryFactory remoteSegmentStoreDirectoryFactory; @Before public void setup() { repositoriesServiceSupplier = mock(Supplier.class); repositoriesService = mock(RepositoriesService.class); when(repositoriesServiceSupplier.get()).thenReturn(repositoriesService); - remoteDirectoryFactory = new RemoteDirectoryFactory(repositoriesServiceSupplier); + remoteSegmentStoreDirectoryFactory = new RemoteSegmentStoreDirectoryFactory(repositoriesServiceSupplier); } public void testNewDirectory() throws IOException { - Settings settings = Settings.builder().build(); + Settings settings = Settings.builder().put(IndexMetadata.SETTING_INDEX_UUID, "uuid_1").build(); IndexSettings indexSettings = IndexSettingsModule.newIndexSettings("foo", settings); Path tempDir = createTempDir().resolve(indexSettings.getUUID()).resolve("0"); ShardPath shardPath = new ShardPath(false, tempDir, tempDir, new ShardId(indexSettings.getIndex(), 0)); @@ -57,20 +60,21 @@ public void testNewDirectory() throws IOException { BlobStore blobStore = mock(BlobStore.class); BlobContainer blobContainer = mock(BlobContainer.class); when(repository.blobStore()).thenReturn(blobStore); + when(repository.basePath()).thenReturn(new BlobPath().add("base_path")); when(blobStore.blobContainer(any())).thenReturn(blobContainer); when(blobContainer.listBlobs()).thenReturn(Collections.emptyMap()); when(repositoriesService.repository("remote_store_repository")).thenReturn(repository); - try (Directory directory = remoteDirectoryFactory.newDirectory("remote_store_repository", indexSettings, shardPath)) { - assertTrue(directory instanceof RemoteDirectory); + try (Directory directory = remoteSegmentStoreDirectoryFactory.newDirectory("remote_store_repository", indexSettings, shardPath)) { + assertTrue(directory instanceof RemoteSegmentStoreDirectory); ArgumentCaptor blobPathCaptor = ArgumentCaptor.forClass(BlobPath.class); - verify(blobStore).blobContainer(blobPathCaptor.capture()); - BlobPath blobPath = blobPathCaptor.getValue(); - assertEquals("foo/0/", blobPath.buildAsString()); + verify(blobStore, times(2)).blobContainer(blobPathCaptor.capture()); + List blobPaths = blobPathCaptor.getAllValues(); + assertEquals("base_path/uuid_1/0/segments/data/", blobPaths.get(0).buildAsString()); + assertEquals("base_path/uuid_1/0/segments/metadata/", blobPaths.get(1).buildAsString()); - directory.listAll(); - verify(blobContainer).listBlobs(); + verify(blobContainer).listBlobsByPrefix(RemoteSegmentStoreDirectory.MetadataFilenameUtils.METADATA_PREFIX); verify(repositoriesService).repository("remote_store_repository"); } } @@ -85,7 +89,7 @@ public void testNewDirectoryRepositoryDoesNotExist() { assertThrows( IllegalArgumentException.class, - () -> remoteDirectoryFactory.newDirectory("remote_store_repository", indexSettings, shardPath) + () -> remoteSegmentStoreDirectoryFactory.newDirectory("remote_store_repository", indexSettings, shardPath) ); } diff --git a/server/src/test/java/org/opensearch/index/store/RemoteSegmentStoreDirectoryTests.java b/server/src/test/java/org/opensearch/index/store/RemoteSegmentStoreDirectoryTests.java index 4eabfa74625f2..96f14616fb54b 100644 --- a/server/src/test/java/org/opensearch/index/store/RemoteSegmentStoreDirectoryTests.java +++ b/server/src/test/java/org/opensearch/index/store/RemoteSegmentStoreDirectoryTests.java @@ -15,6 +15,7 @@ import org.apache.lucene.store.IndexOutput; import org.apache.lucene.tests.util.LuceneTestCase; import org.junit.Before; +import org.opensearch.common.UUIDs; import org.opensearch.common.collect.Set; import org.opensearch.test.OpenSearchTestCase; @@ -129,26 +130,52 @@ public void testInitNoMetadataFile() throws IOException { private Map getDummyMetadata(String prefix, int commitGeneration) { Map metadata = new HashMap<>(); - metadata.put(prefix + ".cfe", prefix + ".cfe::" + prefix + ".cfe__qrt::" + randomIntBetween(1000, 5000)); - metadata.put(prefix + ".cfs", prefix + ".cfs::" + prefix + ".cfs__zxd::" + randomIntBetween(1000, 5000)); - metadata.put(prefix + ".si", prefix + ".si::" + prefix + ".si__yui::" + randomIntBetween(1000, 5000)); + + metadata.put(prefix + ".cfe", prefix + ".cfe::" + prefix + ".cfe__" + UUIDs.base64UUID() + "::" + randomIntBetween(1000, 5000)); + metadata.put(prefix + ".cfs", prefix + ".cfs::" + prefix + ".cfs__" + UUIDs.base64UUID() + "::" + randomIntBetween(1000, 5000)); + metadata.put(prefix + ".si", prefix + ".si::" + prefix + ".si__" + UUIDs.base64UUID() + "::" + randomIntBetween(1000, 5000)); metadata.put( "segments_" + commitGeneration, - "segments_" + commitGeneration + "::segments_" + commitGeneration + "__exv::" + randomIntBetween(1000, 5000) + "segments_" + + commitGeneration + + "::segments_" + + commitGeneration + + "__" + + UUIDs.base64UUID() + + "::" + + randomIntBetween(1000, 5000) ); return metadata; } - private void populateMetadata() throws IOException { + private Map> populateMetadata() throws IOException { List metadataFiles = List.of("metadata__1__5__abc", "metadata__1__6__pqr", "metadata__2__1__zxv"); when(remoteMetadataDirectory.listFilesByPrefix(RemoteSegmentStoreDirectory.MetadataFilenameUtils.METADATA_PREFIX)).thenReturn( metadataFiles ); - IndexInput indexInput = mock(IndexInput.class); - Map dummyMetadata = getDummyMetadata("_0", 1); - when(indexInput.readMapOfStrings()).thenReturn(dummyMetadata); - when(remoteMetadataDirectory.openInput("metadata__2__1__zxv", IOContext.DEFAULT)).thenReturn(indexInput); + Map> metadataFilenameContentMapping = Map.of( + "metadata__1__5__abc", + getDummyMetadata("_0", 1), + "metadata__1__6__pqr", + getDummyMetadata("_0", 1), + "metadata__2__1__zxv", + getDummyMetadata("_0", 1) + ); + + IndexInput indexInput1 = mock(IndexInput.class); + when(indexInput1.readMapOfStrings()).thenReturn(metadataFilenameContentMapping.get("metadata__1__5__abc")); + when(remoteMetadataDirectory.openInput("metadata__1__5__abc", IOContext.DEFAULT)).thenReturn(indexInput1); + + IndexInput indexInput2 = mock(IndexInput.class); + when(indexInput2.readMapOfStrings()).thenReturn(metadataFilenameContentMapping.get("metadata__1__6__pqr")); + when(remoteMetadataDirectory.openInput("metadata__1__6__pqr", IOContext.DEFAULT)).thenReturn(indexInput2); + + IndexInput indexInput3 = mock(IndexInput.class); + when(indexInput3.readMapOfStrings()).thenReturn(metadataFilenameContentMapping.get("metadata__2__1__zxv")); + when(remoteMetadataDirectory.openInput("metadata__2__1__zxv", IOContext.DEFAULT)).thenReturn(indexInput3); + + return metadataFilenameContentMapping; } public void testInit() throws IOException { @@ -291,20 +318,39 @@ public void testCopyFromException() throws IOException { } public void testContainsFile() throws IOException { - populateMetadata(); + List metadataFiles = List.of("metadata__1__5__abc"); + when(remoteMetadataDirectory.listFilesByPrefix(RemoteSegmentStoreDirectory.MetadataFilenameUtils.METADATA_PREFIX)).thenReturn( + metadataFiles + ); + + Map metadata = new HashMap<>(); + metadata.put("_0.cfe", "_0.cfe::_0.cfe__" + UUIDs.base64UUID() + "::1234"); + metadata.put("_0.cfs", "_0.cfs::_0.cfs__" + UUIDs.base64UUID() + "::2345"); + + Map> metadataFilenameContentMapping = Map.of("metadata__1__5__abc", metadata); + + IndexInput indexInput1 = mock(IndexInput.class); + when(indexInput1.readMapOfStrings()).thenReturn(metadataFilenameContentMapping.get("metadata__1__5__abc")); + when(remoteMetadataDirectory.openInput("metadata__1__5__abc", IOContext.DEFAULT)).thenReturn(indexInput1); + remoteSegmentStoreDirectory.init(); - // This is not the correct way to add files but the other way is to open up access to fields in UploadedSegmentMetadata Map uploadedSegmentMetadataMap = remoteSegmentStoreDirectory .getSegmentsUploadedToRemoteStore(); - uploadedSegmentMetadataMap.put( - "_100.si", - new RemoteSegmentStoreDirectory.UploadedSegmentMetadata("_100.si", "_100.si__uuid1", "1234") + + assertThrows( + UnsupportedOperationException.class, + () -> uploadedSegmentMetadataMap.put( + "_100.si", + new RemoteSegmentStoreDirectory.UploadedSegmentMetadata("_100.si", "_100.si__uuid1", "1234") + ) ); - assertTrue(remoteSegmentStoreDirectory.containsFile("_100.si", "1234")); - assertFalse(remoteSegmentStoreDirectory.containsFile("_100.si", "2345")); - assertFalse(remoteSegmentStoreDirectory.containsFile("_200.si", "1234")); + assertTrue(remoteSegmentStoreDirectory.containsFile("_0.cfe", "1234")); + assertTrue(remoteSegmentStoreDirectory.containsFile("_0.cfs", "2345")); + assertFalse(remoteSegmentStoreDirectory.containsFile("_0.cfe", "1234000")); + assertFalse(remoteSegmentStoreDirectory.containsFile("_0.cfs", "2345000")); + assertFalse(remoteSegmentStoreDirectory.containsFile("_0.si", "23")); } public void testUploadMetadataEmpty() throws IOException { @@ -336,4 +382,84 @@ public void testUploadMetadataNonEmpty() throws IOException { String metadataString = remoteSegmentStoreDirectory.getSegmentsUploadedToRemoteStore().get("_0.si").toString(); verify(indexOutput).writeMapOfStrings(Map.of("_0.si", metadataString)); } + + public void testDeleteStaleCommitsException() throws IOException { + when(remoteMetadataDirectory.listFilesByPrefix(RemoteSegmentStoreDirectory.MetadataFilenameUtils.METADATA_PREFIX)).thenThrow( + new IOException("Error reading") + ); + + assertThrows(IOException.class, () -> remoteSegmentStoreDirectory.deleteStaleSegments(5)); + } + + public void testDeleteStaleCommitsWithinThreshold() throws IOException { + populateMetadata(); + + // popluateMetadata() adds stub to return 3 metadata files + // We are passing lastNMetadataFilesToKeep=5 here so that none of the metadata files will be deleted + remoteSegmentStoreDirectory.deleteStaleSegments(5); + + verify(remoteMetadataDirectory, times(0)).openInput(any(String.class), eq(IOContext.DEFAULT)); + } + + public void testDeleteStaleCommitsActualDelete() throws IOException { + Map> metadataFilenameContentMapping = populateMetadata(); + remoteSegmentStoreDirectory.init(); + + // popluateMetadata() adds stub to return 3 metadata files + // We are passing lastNMetadataFilesToKeep=2 here so that oldest 1 metadata file will be deleted + remoteSegmentStoreDirectory.deleteStaleSegments(2); + + for (String metadata : metadataFilenameContentMapping.get("metadata__1__5__abc").values()) { + String uploadedFilename = metadata.split(RemoteSegmentStoreDirectory.UploadedSegmentMetadata.SEPARATOR)[1]; + verify(remoteDataDirectory).deleteFile(uploadedFilename); + } + ; + verify(remoteMetadataDirectory).deleteFile("metadata__1__5__abc"); + } + + public void testDeleteStaleCommitsActualDeleteIOException() throws IOException { + Map> metadataFilenameContentMapping = populateMetadata(); + remoteSegmentStoreDirectory.init(); + + String segmentFileWithException = metadataFilenameContentMapping.get("metadata__1__5__abc") + .values() + .stream() + .findAny() + .get() + .split(RemoteSegmentStoreDirectory.UploadedSegmentMetadata.SEPARATOR)[1]; + doThrow(new IOException("Error")).when(remoteDataDirectory).deleteFile(segmentFileWithException); + // popluateMetadata() adds stub to return 3 metadata files + // We are passing lastNMetadataFilesToKeep=2 here so that oldest 1 metadata file will be deleted + remoteSegmentStoreDirectory.deleteStaleSegments(2); + + for (String metadata : metadataFilenameContentMapping.get("metadata__1__5__abc").values()) { + String uploadedFilename = metadata.split(RemoteSegmentStoreDirectory.UploadedSegmentMetadata.SEPARATOR)[1]; + verify(remoteDataDirectory).deleteFile(uploadedFilename); + } + ; + verify(remoteMetadataDirectory, times(0)).deleteFile("metadata__1__5__abc"); + } + + public void testDeleteStaleCommitsActualDeleteNoSuchFileException() throws IOException { + Map> metadataFilenameContentMapping = populateMetadata(); + remoteSegmentStoreDirectory.init(); + + String segmentFileWithException = metadataFilenameContentMapping.get("metadata__1__5__abc") + .values() + .stream() + .findAny() + .get() + .split(RemoteSegmentStoreDirectory.UploadedSegmentMetadata.SEPARATOR)[1]; + doThrow(new NoSuchFileException(segmentFileWithException)).when(remoteDataDirectory).deleteFile(segmentFileWithException); + // popluateMetadata() adds stub to return 3 metadata files + // We are passing lastNMetadataFilesToKeep=2 here so that oldest 1 metadata file will be deleted + remoteSegmentStoreDirectory.deleteStaleSegments(2); + + for (String metadata : metadataFilenameContentMapping.get("metadata__1__5__abc").values()) { + String uploadedFilename = metadata.split(RemoteSegmentStoreDirectory.UploadedSegmentMetadata.SEPARATOR)[1]; + verify(remoteDataDirectory).deleteFile(uploadedFilename); + } + ; + verify(remoteMetadataDirectory).deleteFile("metadata__1__5__abc"); + } } diff --git a/server/src/test/java/org/opensearch/snapshots/SnapshotResiliencyTests.java b/server/src/test/java/org/opensearch/snapshots/SnapshotResiliencyTests.java index 4b8eec70f2c1a..4d3b841e203de 100644 --- a/server/src/test/java/org/opensearch/snapshots/SnapshotResiliencyTests.java +++ b/server/src/test/java/org/opensearch/snapshots/SnapshotResiliencyTests.java @@ -172,7 +172,7 @@ import org.opensearch.index.seqno.GlobalCheckpointSyncAction; import org.opensearch.index.seqno.RetentionLeaseSyncer; import org.opensearch.index.shard.PrimaryReplicaSyncer; -import org.opensearch.index.store.RemoteDirectoryFactory; +import org.opensearch.index.store.RemoteSegmentStoreDirectoryFactory; import org.opensearch.indices.IndicesModule; import org.opensearch.indices.IndicesService; import org.opensearch.indices.ShardLimitValidator; @@ -1826,7 +1826,7 @@ public void onFailure(final Exception e) { emptyMap(), null, emptyMap(), - new RemoteDirectoryFactory(() -> repositoriesService) + new RemoteSegmentStoreDirectoryFactory(() -> repositoriesService) ); final RecoverySettings recoverySettings = new RecoverySettings(settings, clusterSettings); snapshotShardsService = new SnapshotShardsService( diff --git a/test/framework/src/main/java/org/opensearch/index/shard/IndexShardTestCase.java b/test/framework/src/main/java/org/opensearch/index/shard/IndexShardTestCase.java index f446538acccbb..08004b7e42fea 100644 --- a/test/framework/src/main/java/org/opensearch/index/shard/IndexShardTestCase.java +++ b/test/framework/src/main/java/org/opensearch/index/shard/IndexShardTestCase.java @@ -59,6 +59,10 @@ import org.opensearch.common.CheckedFunction; import org.opensearch.common.Nullable; import org.opensearch.common.UUIDs; +import org.opensearch.common.blobstore.BlobContainer; +import org.opensearch.common.blobstore.BlobPath; +import org.opensearch.common.blobstore.fs.FsBlobContainer; +import org.opensearch.common.blobstore.fs.FsBlobStore; import org.opensearch.common.bytes.BytesArray; import org.opensearch.common.concurrent.GatedCloseable; import org.opensearch.common.lucene.uid.Versions; @@ -88,6 +92,8 @@ import org.opensearch.index.seqno.SequenceNumbers; import org.opensearch.index.similarity.SimilarityService; import org.opensearch.index.snapshots.IndexShardSnapshotStatus; +import org.opensearch.index.store.RemoteDirectory; +import org.opensearch.index.store.RemoteSegmentStoreDirectory; import org.opensearch.index.store.Store; import org.opensearch.index.store.StoreFileMetadata; import org.opensearch.index.translog.InternalTranslogFactory; @@ -123,6 +129,7 @@ import org.opensearch.threadpool.ThreadPool; import java.io.IOException; +import java.nio.file.Path; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; @@ -532,7 +539,10 @@ protected IndexShard newShard( ShardId shardId = shardPath.getShardId(); NodeEnvironment.NodePath remoteNodePath = new NodeEnvironment.NodePath(createTempDir()); ShardPath remoteShardPath = new ShardPath(false, remoteNodePath.resolve(shardId), remoteNodePath.resolve(shardId), shardId); - storeProvider = is -> createStore(is, remoteShardPath); + RemoteDirectory dataDirectory = newRemoteDirectory(remoteShardPath.resolveIndex()); + RemoteDirectory metadataDirectory = newRemoteDirectory(remoteShardPath.resolveIndex()); + RemoteSegmentStoreDirectory remoteSegmentStoreDirectory = new RemoteSegmentStoreDirectory(dataDirectory, metadataDirectory); + storeProvider = is -> createStore(shardId, is, remoteSegmentStoreDirectory); remoteStore = storeProvider.apply(indexSettings); } indexShard = new IndexShard( @@ -570,6 +580,13 @@ protected IndexShard newShard( return indexShard; } + private RemoteDirectory newRemoteDirectory(Path f) throws IOException { + FsBlobStore fsBlobStore = new FsBlobStore(1024, f, false); + BlobPath blobPath = new BlobPath(); + BlobContainer fsBlobContainer = new FsBlobContainer(fsBlobStore, blobPath, f); + return new RemoteDirectory(fsBlobContainer); + } + /** * Takes an existing shard, closes it and starts a new initialing shard at the same location * From 7fe5830798b43f919ba1beed8669b711b149e60d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Vl=C4=8Dek?= Date: Mon, 29 Aug 2022 21:17:21 +0200 Subject: [PATCH 017/187] ZIP publication groupId value is configurable (#4156) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When publishing Zip POM the groupId value was hard-coded to `org.opensearch.plugin` value which worked fine for existing core plugins but is not convenient for other plugins (such as community plugins maintained in independent repositories). This PR changes the sources of the ZIP publishing groupId value. Specifically, there are two ways to set the value: 1) It is automatically inherited from the Gradle "project.group" 2) It can be manually specified in the ZIP publication POM object This PR also brings a major rework of tests in PublishTests class. Individual testing scenarios are driven by "real" gradle building scripts (utilizing `java-gradle-plugin` gradle plugin). Closes #3692 Signed-off-by: Lukáš Vlček Signed-off-by: Lukáš Vlček --- CHANGELOG.md | 1 + .../opensearch/gradle/pluginzip/Publish.java | 44 +-- .../gradle/pluginzip/PublishTests.java | 339 +++++++++++------- .../pluginzip/customizedGroupValue.gradle | 45 +++ .../customizedInvalidGroupValue.gradle | 45 +++ .../pluginzip/groupAndVersionValue.gradle | 44 +++ .../pluginzip/missingGroupValue.gradle | 22 ++ .../pluginzip/missingPOMEntity.gradle | 22 ++ 8 files changed, 406 insertions(+), 156 deletions(-) create mode 100644 buildSrc/src/test/resources/pluginzip/customizedGroupValue.gradle create mode 100644 buildSrc/src/test/resources/pluginzip/customizedInvalidGroupValue.gradle create mode 100644 buildSrc/src/test/resources/pluginzip/groupAndVersionValue.gradle create mode 100644 buildSrc/src/test/resources/pluginzip/missingGroupValue.gradle create mode 100644 buildSrc/src/test/resources/pluginzip/missingPOMEntity.gradle diff --git a/CHANGELOG.md b/CHANGELOG.md index f11f407434e6b..52fa12d523659 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### Changed - Dependency updates (httpcore, mockito, slf4j, httpasyncclient, commons-codec) ([#4308](https://github.com/opensearch-project/OpenSearch/pull/4308)) - Use RemoteSegmentStoreDirectory instead of RemoteDirectory ([#4240](https://github.com/opensearch-project/OpenSearch/pull/4240)) +- Plugin ZIP publication groupId value is configurable ([#4156](https://github.com/opensearch-project/OpenSearch/pull/4156)) ### Deprecated diff --git a/buildSrc/src/main/java/org/opensearch/gradle/pluginzip/Publish.java b/buildSrc/src/main/java/org/opensearch/gradle/pluginzip/Publish.java index d83384ec7d172..70c3737ba3674 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/pluginzip/Publish.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/pluginzip/Publish.java @@ -9,7 +9,8 @@ import org.gradle.api.Plugin; import org.gradle.api.Project; -import org.gradle.api.publish.Publication; +import org.gradle.api.logging.Logger; +import org.gradle.api.logging.Logging; import org.gradle.api.publish.PublishingExtension; import org.gradle.api.publish.maven.MavenPublication; import org.gradle.api.publish.maven.plugins.MavenPublishPlugin; @@ -18,6 +19,9 @@ import org.gradle.api.Task; public class Publish implements Plugin { + + private static final Logger LOGGER = Logging.getLogger(Publish.class); + public final static String EXTENSION_NAME = "zipmavensettings"; public final static String PUBLICATION_NAME = "pluginZip"; public final static String STAGING_REPO = "zipStaging"; @@ -37,27 +41,25 @@ public static void configMaven(Project project) { }); }); publishing.publications(publications -> { - final Publication publication = publications.findByName(PUBLICATION_NAME); - if (publication == null) { - publications.create(PUBLICATION_NAME, MavenPublication.class, mavenZip -> { - String zipGroup = "org.opensearch.plugin"; - String zipArtifact = project.getName(); - String zipVersion = getProperty("version", project); - mavenZip.artifact(project.getTasks().named("bundlePlugin")); - mavenZip.setGroupId(zipGroup); - mavenZip.setArtifactId(zipArtifact); - mavenZip.setVersion(zipVersion); - }); - } else { - final MavenPublication mavenZip = (MavenPublication) publication; - String zipGroup = "org.opensearch.plugin"; - String zipArtifact = project.getName(); - String zipVersion = getProperty("version", project); - mavenZip.artifact(project.getTasks().named("bundlePlugin")); - mavenZip.setGroupId(zipGroup); - mavenZip.setArtifactId(zipArtifact); - mavenZip.setVersion(zipVersion); + MavenPublication mavenZip = (MavenPublication) publications.findByName(PUBLICATION_NAME); + + if (mavenZip == null) { + mavenZip = publications.create(PUBLICATION_NAME, MavenPublication.class); } + + String groupId = mavenZip.getGroupId(); + if (groupId == null) { + // The groupId is not customized thus we get the value from "project.group". + // See https://docs.gradle.org/current/userguide/publishing_maven.html#sec:identity_values_in_the_generated_pom + groupId = getProperty("group", project); + } + + String artifactId = project.getName(); + String pluginVersion = getProperty("version", project); + mavenZip.artifact(project.getTasks().named("bundlePlugin")); + mavenZip.setGroupId(groupId); + mavenZip.setArtifactId(artifactId); + mavenZip.setVersion(pluginVersion); }); }); } diff --git a/buildSrc/src/test/java/org/opensearch/gradle/pluginzip/PublishTests.java b/buildSrc/src/test/java/org/opensearch/gradle/pluginzip/PublishTests.java index 8c1314c4b4394..06632e2dfa476 100644 --- a/buildSrc/src/test/java/org/opensearch/gradle/pluginzip/PublishTests.java +++ b/buildSrc/src/test/java/org/opensearch/gradle/pluginzip/PublishTests.java @@ -10,19 +10,21 @@ import org.gradle.testkit.runner.BuildResult; import org.gradle.testkit.runner.GradleRunner; -import org.gradle.testfixtures.ProjectBuilder; -import org.gradle.api.Project; +import org.gradle.testkit.runner.UnexpectedBuildFailure; import org.opensearch.gradle.test.GradleUnitTestCase; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.rules.TemporaryFolder; -import java.io.IOException; -import org.gradle.api.publish.maven.tasks.PublishToMavenRepository; import java.io.File; +import java.io.FileReader; import java.io.FileWriter; +import java.io.IOException; import java.io.Writer; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import static org.gradle.testkit.runner.TaskOutcome.SUCCESS; @@ -30,14 +32,16 @@ import org.apache.maven.model.Model; import org.apache.maven.model.io.xpp3.MavenXpp3Reader; import org.codehaus.plexus.util.xml.pull.XmlPullParserException; -import java.io.FileReader; -import org.gradle.api.tasks.bundling.Zip; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.List; -import java.util.ArrayList; public class PublishTests extends GradleUnitTestCase { private TemporaryFolder projectDir; + private static final String TEMPLATE_RESOURCE_FOLDER = "pluginzip"; + private final String PROJECT_NAME = "sample-plugin"; + private final String ZIP_PUBLISH_TASK = "publishPluginZipPublicationToZipStagingRepository"; @Before public void setUp() throws IOException { @@ -51,155 +55,200 @@ public void tearDown() { } @Test - public void testZipPublish() throws IOException, XmlPullParserException { - String zipPublishTask = "publishPluginZipPublicationToZipStagingRepository"; - prepareProjectForPublishTask(zipPublishTask); - - // Generate the build.gradle file - String buildFileContent = "apply plugin: 'maven-publish' \n" - + "apply plugin: 'java' \n" - + "publishing {\n" - + " repositories {\n" - + " maven {\n" - + " url = 'local-staging-repo/'\n" - + " name = 'zipStaging'\n" - + " }\n" - + " }\n" - + " publications {\n" - + " pluginZip(MavenPublication) {\n" - + " groupId = 'org.opensearch.plugin' \n" - + " artifactId = 'sample-plugin' \n" - + " version = '2.0.0.0' \n" - + " artifact('sample-plugin.zip') \n" - + " }\n" - + " }\n" - + "}"; - writeString(projectDir.newFile("build.gradle"), buildFileContent); - // Execute the task publishPluginZipPublicationToZipStagingRepository - List allArguments = new ArrayList(); - allArguments.add("build"); - allArguments.add(zipPublishTask); - GradleRunner runner = GradleRunner.create(); - runner.forwardOutput(); - runner.withPluginClasspath(); - runner.withArguments(allArguments); - runner.withProjectDir(projectDir.getRoot()); + public void missingGroupValue() throws IOException, URISyntaxException, XmlPullParserException { + GradleRunner runner = prepareGradleRunnerFromTemplate("missingGroupValue.gradle"); + Exception e = assertThrows(UnexpectedBuildFailure.class, runner::build); + assertTrue(e.getMessage().contains("Invalid publication 'pluginZip': groupId cannot be empty.")); + } + + /** + * This would be the most common use case where user declares Maven publication entity with basic info + * and the resulting POM file will use groupId and version values from the Gradle project object. + */ + @Test + public void groupAndVersionValue() throws IOException, URISyntaxException, XmlPullParserException { + GradleRunner runner = prepareGradleRunnerFromTemplate("groupAndVersionValue.gradle"); BuildResult result = runner.build(); - // Check if task publishMavenzipPublicationToZipstagingRepository has ran well - assertEquals(SUCCESS, result.task(":" + zipPublishTask).getOutcome()); - // check if the zip has been published to local staging repo + + /** Check if build and {@value ZIP_PUBLISH_TASK} tasks have run well */ + assertEquals(SUCCESS, result.task(":" + "build").getOutcome()); + assertEquals(SUCCESS, result.task(":" + ZIP_PUBLISH_TASK).getOutcome()); + + // check if both the zip and pom files have been published to local staging repo assertTrue( - new File(projectDir.getRoot(), "local-staging-repo/org/opensearch/plugin/sample-plugin/2.0.0.0/sample-plugin-2.0.0.0.zip") - .exists() + new File( + projectDir.getRoot(), + String.join( + File.separator, + "build", + "local-staging-repo", + "org", + "custom", + "group", + PROJECT_NAME, + "2.0.0.0", + PROJECT_NAME + "-2.0.0.0.pom" + ) + ).exists() ); - assertEquals(SUCCESS, result.task(":" + "build").getOutcome()); - // Parse the maven file and validate the groupID to org.opensearch.plugin + assertTrue( + new File( + projectDir.getRoot(), + String.join( + File.separator, + "build", + "local-staging-repo", + "org", + "custom", + "group", + PROJECT_NAME, + "2.0.0.0", + PROJECT_NAME + "-2.0.0.0.zip" + ) + ).exists() + ); + + // Parse the maven file and validate the groupID MavenXpp3Reader reader = new MavenXpp3Reader(); Model model = reader.read( new FileReader( - new File(projectDir.getRoot(), "local-staging-repo/org/opensearch/plugin/sample-plugin/2.0.0.0/sample-plugin-2.0.0.0.pom") + new File( + projectDir.getRoot(), + String.join( + File.separator, + "build", + "local-staging-repo", + "org", + "custom", + "group", + PROJECT_NAME, + "2.0.0.0", + PROJECT_NAME + "-2.0.0.0.pom" + ) + ) ) ); - assertEquals(model.getGroupId(), "org.opensearch.plugin"); + assertEquals(model.getVersion(), "2.0.0.0"); + assertEquals(model.getGroupId(), "org.custom.group"); + assertEquals(model.getUrl(), "https://github.com/doe/sample-plugin"); } + /** + * In this case the Publication entity is completely missing but still the POM file is generated using the default + * values including the groupId and version values obtained from the Gradle project object. + */ @Test - public void testZipPublishWithPom() throws IOException, XmlPullParserException { - String zipPublishTask = "publishPluginZipPublicationToZipStagingRepository"; - Project project = prepareProjectForPublishTask(zipPublishTask); - - // Generate the build.gradle file - String buildFileContent = "apply plugin: 'maven-publish' \n" - + "apply plugin: 'java' \n" - + "publishing {\n" - + " repositories {\n" - + " maven {\n" - + " url = 'local-staging-repo/'\n" - + " name = 'zipStaging'\n" - + " }\n" - + " }\n" - + " publications {\n" - + " pluginZip(MavenPublication) {\n" - + " groupId = 'org.opensearch.plugin' \n" - + " artifactId = 'sample-plugin' \n" - + " version = '2.0.0.0' \n" - + " artifact('sample-plugin.zip') \n" - + " pom {\n" - + " name = 'sample-plugin'\n" - + " description = 'sample-description'\n" - + " licenses {\n" - + " license {\n" - + " name = \"The Apache License, Version 2.0\"\n" - + " url = \"http://www.apache.org/licenses/LICENSE-2.0.txt\"\n" - + " }\n" - + " }\n" - + " developers {\n" - + " developer {\n" - + " name = 'opensearch'\n" - + " url = 'https://github.com/opensearch-project/OpenSearch'\n" - + " }\n" - + " }\n" - + " url = 'https://github.com/opensearch-project/OpenSearch'\n" - + " scm {\n" - + " url = 'https://github.com/opensearch-project/OpenSearch'\n" - + " }\n" - + " }" - + " }\n" - + " }\n" - + "}"; - writeString(projectDir.newFile("build.gradle"), buildFileContent); - // Execute the task publishPluginZipPublicationToZipStagingRepository - List allArguments = new ArrayList(); - allArguments.add("build"); - allArguments.add(zipPublishTask); - GradleRunner runner = GradleRunner.create(); - runner.forwardOutput(); - runner.withPluginClasspath(); - runner.withArguments(allArguments); - runner.withProjectDir(projectDir.getRoot()); + public void missingPOMEntity() throws IOException, URISyntaxException, XmlPullParserException { + GradleRunner runner = prepareGradleRunnerFromTemplate("missingPOMEntity.gradle"); BuildResult result = runner.build(); - // Check if task publishMavenzipPublicationToZipstagingRepository has ran well - assertEquals(SUCCESS, result.task(":" + zipPublishTask).getOutcome()); - // check if the zip has been published to local staging repo - assertTrue( - new File(projectDir.getRoot(), "local-staging-repo/org/opensearch/plugin/sample-plugin/2.0.0.0/sample-plugin-2.0.0.0.zip") - .exists() + + /** Check if build and {@value ZIP_PUBLISH_TASK} tasks have run well */ + assertEquals(SUCCESS, result.task(":" + "build").getOutcome()); + assertEquals(SUCCESS, result.task(":" + ZIP_PUBLISH_TASK).getOutcome()); + + // Parse the maven file and validate it + MavenXpp3Reader reader = new MavenXpp3Reader(); + Model model = reader.read( + new FileReader( + new File( + projectDir.getRoot(), + String.join( + File.separator, + "build", + "local-staging-repo", + "org", + "custom", + "group", + PROJECT_NAME, + "2.0.0.0", + PROJECT_NAME + "-2.0.0.0.pom" + ) + ) + ) ); + + assertEquals(model.getArtifactId(), PROJECT_NAME); + assertEquals(model.getGroupId(), "org.custom.group"); + assertEquals(model.getVersion(), "2.0.0.0"); + assertEquals(model.getPackaging(), "zip"); + + assertNull(model.getName()); + assertNull(model.getDescription()); + + assertEquals(0, model.getDevelopers().size()); + assertEquals(0, model.getContributors().size()); + assertEquals(0, model.getLicenses().size()); + } + + /** + * In some cases we need the POM groupId value to be different from the Gradle "project.group" value hence we + * allow for groupId customization (it will override whatever the Gradle "project.group" value is). + */ + @Test + public void customizedGroupValue() throws IOException, URISyntaxException, XmlPullParserException { + GradleRunner runner = prepareGradleRunnerFromTemplate("customizedGroupValue.gradle"); + BuildResult result = runner.build(); + + /** Check if build and {@value ZIP_PUBLISH_TASK} tasks have run well */ assertEquals(SUCCESS, result.task(":" + "build").getOutcome()); - // Parse the maven file and validate the groupID to org.opensearch.plugin + assertEquals(SUCCESS, result.task(":" + ZIP_PUBLISH_TASK).getOutcome()); + + // Parse the maven file and validate the groupID MavenXpp3Reader reader = new MavenXpp3Reader(); Model model = reader.read( new FileReader( - new File(projectDir.getRoot(), "local-staging-repo/org/opensearch/plugin/sample-plugin/2.0.0.0/sample-plugin-2.0.0.0.pom") + new File( + projectDir.getRoot(), + String.join( + File.separator, + "build", + "local-staging-repo", + "I", + "am", + "customized", + PROJECT_NAME, + "2.0.0.0", + PROJECT_NAME + "-2.0.0.0.pom" + ) + ) ) ); - assertEquals(model.getGroupId(), "org.opensearch.plugin"); - assertEquals(model.getUrl(), "https://github.com/opensearch-project/OpenSearch"); + + assertEquals(model.getGroupId(), "I.am.customized"); } - protected Project prepareProjectForPublishTask(String zipPublishTask) throws IOException { - Project project = ProjectBuilder.builder().build(); - - // Apply the opensearch.pluginzip plugin - project.getPluginManager().apply("opensearch.pluginzip"); - // Check if the plugin has been applied to the project - assertTrue(project.getPluginManager().hasPlugin("opensearch.pluginzip")); - // Check if the project has the task from class PublishToMavenRepository after plugin apply - assertNotNull(project.getTasks().withType(PublishToMavenRepository.class)); - // Create a mock bundlePlugin task - Zip task = project.getTasks().create("bundlePlugin", Zip.class); - Publish.configMaven(project); - // Check if the main task publishPluginZipPublicationToZipStagingRepository exists after plugin apply - assertTrue(project.getTasks().getNames().contains(zipPublishTask)); - assertNotNull("Task to generate: ", project.getTasks().getByName(zipPublishTask)); - // Run Gradle functional tests, but calling a build.gradle file, that resembles the plugin publish behavior - - // Create a sample plugin zip file - File sampleZip = new File(projectDir.getRoot(), "sample-plugin.zip"); - Files.createFile(sampleZip.toPath()); - writeString(projectDir.newFile("settings.gradle"), ""); - - return project; + /** + * If the customized groupId value is invalid (from the Maven POM perspective) then we need to be sure it is + * caught and reported properly. + */ + @Test + public void customizedInvalidGroupValue() throws IOException, URISyntaxException { + GradleRunner runner = prepareGradleRunnerFromTemplate("customizedInvalidGroupValue.gradle"); + Exception e = assertThrows(UnexpectedBuildFailure.class, runner::build); + assertTrue( + e.getMessage().contains("Invalid publication 'pluginZip': groupId ( ) is not a valid Maven identifier ([A-Za-z0-9_\\-.]+).") + ); + } + + private GradleRunner prepareGradleRunnerFromTemplate(String templateName) throws IOException, URISyntaxException { + useTemplateFile(projectDir.newFile("build.gradle"), templateName); + prepareGradleFilesAndSources(); + + GradleRunner runner = GradleRunner.create() + .forwardOutput() + .withPluginClasspath() + .withArguments("build", ZIP_PUBLISH_TASK) + .withProjectDir(projectDir.getRoot()); + + return runner; + } + + private void prepareGradleFilesAndSources() throws IOException { + // A dummy "source" file that is processed with bundlePlugin and put into a ZIP artifact file + File bundleFile = new File(projectDir.getRoot(), PROJECT_NAME + "-source.txt"); + Path zipFile = Files.createFile(bundleFile.toPath()); + // Setting a project name via settings.gradle file + writeString(projectDir.newFile("settings.gradle"), "rootProject.name = '" + PROJECT_NAME + "'"); } private void writeString(File file, String string) throws IOException { @@ -208,4 +257,24 @@ private void writeString(File file, String string) throws IOException { } } + /** + * Write the content of the "template" file into the target file. + * The template file must be located in the {@value TEMPLATE_RESOURCE_FOLDER} folder. + * @param targetFile A target file + * @param templateFile A name of the template file located under {@value TEMPLATE_RESOURCE_FOLDER} folder + */ + private void useTemplateFile(File targetFile, String templateFile) throws IOException, URISyntaxException { + + URL resource = getClass().getClassLoader().getResource(String.join(File.separator, TEMPLATE_RESOURCE_FOLDER, templateFile)); + Path resPath = Paths.get(resource.toURI()).toAbsolutePath(); + List lines = Files.readAllLines(resPath, StandardCharsets.UTF_8); + + try (Writer writer = new FileWriter(targetFile)) { + for (String line : lines) { + writer.write(line); + writer.write(System.lineSeparator()); + } + } + } + } diff --git a/buildSrc/src/test/resources/pluginzip/customizedGroupValue.gradle b/buildSrc/src/test/resources/pluginzip/customizedGroupValue.gradle new file mode 100644 index 0000000000000..1bde3edda2d91 --- /dev/null +++ b/buildSrc/src/test/resources/pluginzip/customizedGroupValue.gradle @@ -0,0 +1,45 @@ +plugins { + id 'java-gradle-plugin' + id 'nebula.maven-base-publish' + id 'opensearch.pluginzip' +} + +group="org.custom.group" +version='2.0.0.0' + +// A bundlePlugin task mockup +tasks.register('bundlePlugin', Zip.class) { + archiveFileName = "sample-plugin-${version}.zip" + destinationDirectory = layout.buildDirectory.dir('distributions') + from layout.projectDirectory.file('sample-plugin-source.txt') +} + +publishing { + publications { + pluginZip(MavenPublication) { + groupId = "I.am.customized" + pom { + name = "sample-plugin" + description = "pluginDescription" + licenses { + license { + name = "The Apache License, Version 2.0" + url = "http://www.apache.org/licenses/LICENSE-2.0.txt" + } + } + developers { + developer { + name = "John Doe" + url = "https://github.com/john-doe/" + organization = "Doe.inc" + organizationUrl = "https://doe.inc/" + } + } + url = "https://github.com/doe/sample-plugin" + scm { + url = "https://github.com/doe/sample-plugin" + } + } + } + } +} diff --git a/buildSrc/src/test/resources/pluginzip/customizedInvalidGroupValue.gradle b/buildSrc/src/test/resources/pluginzip/customizedInvalidGroupValue.gradle new file mode 100644 index 0000000000000..b6deeeb12ca6a --- /dev/null +++ b/buildSrc/src/test/resources/pluginzip/customizedInvalidGroupValue.gradle @@ -0,0 +1,45 @@ +plugins { + id 'java-gradle-plugin' + id 'nebula.maven-base-publish' + id 'opensearch.pluginzip' +} + +group="org.custom.group" +version='2.0.0.0' + +// A bundlePlugin task mockup +tasks.register('bundlePlugin', Zip.class) { + archiveFileName = "sample-plugin-${version}.zip" + destinationDirectory = layout.buildDirectory.dir('distributions') + from layout.projectDirectory.file('sample-plugin-source.txt') +} + +publishing { + publications { + pluginZip(MavenPublication) { + groupId = " " // <-- User provides invalid value + pom { + name = "sample-plugin" + description = "pluginDescription" + licenses { + license { + name = "The Apache License, Version 2.0" + url = "http://www.apache.org/licenses/LICENSE-2.0.txt" + } + } + developers { + developer { + name = "John Doe" + url = "https://github.com/john-doe/" + organization = "Doe.inc" + organizationUrl = "https://doe.inc/" + } + } + url = "https://github.com/doe/sample-plugin" + scm { + url = "https://github.com/doe/sample-plugin" + } + } + } + } +} diff --git a/buildSrc/src/test/resources/pluginzip/groupAndVersionValue.gradle b/buildSrc/src/test/resources/pluginzip/groupAndVersionValue.gradle new file mode 100644 index 0000000000000..bdab385f6082c --- /dev/null +++ b/buildSrc/src/test/resources/pluginzip/groupAndVersionValue.gradle @@ -0,0 +1,44 @@ +plugins { + id 'java-gradle-plugin' + id 'nebula.maven-base-publish' + id 'opensearch.pluginzip' +} + +group="org.custom.group" +version='2.0.0.0' + +// A bundlePlugin task mockup +tasks.register('bundlePlugin', Zip.class) { + archiveFileName = "sample-plugin-${version}.zip" + destinationDirectory = layout.buildDirectory.dir('distributions') + from layout.projectDirectory.file('sample-plugin-source.txt') +} + +publishing { + publications { + pluginZip(MavenPublication) { + pom { + name = "sample-plugin" + description = "pluginDescription" + licenses { + license { + name = "The Apache License, Version 2.0" + url = "http://www.apache.org/licenses/LICENSE-2.0.txt" + } + } + developers { + developer { + name = "John Doe" + url = "https://github.com/john-doe/" + organization = "Doe.inc" + organizationUrl = "https://doe.inc/" + } + } + url = "https://github.com/doe/sample-plugin" + scm { + url = "https://github.com/doe/sample-plugin" + } + } + } + } +} diff --git a/buildSrc/src/test/resources/pluginzip/missingGroupValue.gradle b/buildSrc/src/test/resources/pluginzip/missingGroupValue.gradle new file mode 100644 index 0000000000000..602c178ea1a5b --- /dev/null +++ b/buildSrc/src/test/resources/pluginzip/missingGroupValue.gradle @@ -0,0 +1,22 @@ +plugins { + id 'java-gradle-plugin' + id 'nebula.maven-base-publish' + id 'opensearch.pluginzip' +} + +//group="org.custom.group" +version='2.0.0.0' + +// A bundlePlugin task mockup +tasks.register('bundlePlugin', Zip.class) { + archiveFileName = "sample-plugin-${version}.zip" + destinationDirectory = layout.buildDirectory.dir('distributions') + from layout.projectDirectory.file('sample-plugin-source.txt') +} + +publishing { + publications { + pluginZip(MavenPublication) { + } + } +} diff --git a/buildSrc/src/test/resources/pluginzip/missingPOMEntity.gradle b/buildSrc/src/test/resources/pluginzip/missingPOMEntity.gradle new file mode 100644 index 0000000000000..2cc67c2e98954 --- /dev/null +++ b/buildSrc/src/test/resources/pluginzip/missingPOMEntity.gradle @@ -0,0 +1,22 @@ +plugins { + id 'java-gradle-plugin' + id 'nebula.maven-base-publish' + id 'opensearch.pluginzip' +} + +group="org.custom.group" +version='2.0.0.0' + +// A bundlePlugin task mockup +tasks.register('bundlePlugin', Zip.class) { + archiveFileName = "sample-plugin-${version}.zip" + destinationDirectory = layout.buildDirectory.dir('distributions') + from layout.projectDirectory.file('sample-plugin-source.txt') +} + +publishing { + publications { + pluginZip(MavenPublication) { + } + } +} From f4e041ec5b178db0bb80db167dc99ac3fdc3eb09 Mon Sep 17 00:00:00 2001 From: Suraj Singh Date: Mon, 29 Aug 2022 13:43:44 -0700 Subject: [PATCH 018/187] [Segment Replication] Add timeout on Mockito.verify to reduce flakyness in testReplicationOnDone test (#4314) * [Segment Replication] testReplicationOnDone Add timeout to allow time for verify call Signed-off-by: Suraj Singh * Update changelog Signed-off-by: Suraj Singh * Add change log entry Signed-off-by: Suraj Singh Signed-off-by: Suraj Singh --- CHANGELOG.md | 1 + .../replication/SegmentReplicationTargetServiceTests.java | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52fa12d523659..b3c5d731af082 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - `opensearch.bat` and `opensearch-service.bat install` failing to run, missing logs directory ([#4305](https://github.com/opensearch-project/OpenSearch/pull/4305)) - Restore using the class ClusterInfoRequest and ClusterInfoRequestBuilder from package 'org.opensearch.action.support.master.info' for subclasses ([#4307](https://github.com/opensearch-project/OpenSearch/pull/4307)) - Do not fail replica shard due to primary closure ([#4133](https://github.com/opensearch-project/OpenSearch/pull/4133)) +- Add timeout on Mockito.verify to reduce flakyness in testReplicationOnDone test([#4314](https://github.com/opensearch-project/OpenSearch/pull/4314)) ### Security diff --git a/server/src/test/java/org/opensearch/indices/replication/SegmentReplicationTargetServiceTests.java b/server/src/test/java/org/opensearch/indices/replication/SegmentReplicationTargetServiceTests.java index d3a6d1a97dacc..de739f4ca834a 100644 --- a/server/src/test/java/org/opensearch/indices/replication/SegmentReplicationTargetServiceTests.java +++ b/server/src/test/java/org/opensearch/indices/replication/SegmentReplicationTargetServiceTests.java @@ -252,9 +252,8 @@ public void testReplicationOnDone() throws IOException { SegmentReplicationTargetService.SegmentReplicationListener listener = captor.getValue(); listener.onDone(new SegmentReplicationState(new ReplicationLuceneIndex())); doNothing().when(spy).onNewCheckpoint(any(), any()); - verify(spy, timeout(0).times(2)).onNewCheckpoint(eq(anotherNewCheckpoint), any()); + verify(spy, timeout(100).times(2)).onNewCheckpoint(eq(anotherNewCheckpoint), any()); closeShard(indexShard, false); - } public void testBeforeIndexShardClosed_CancelsOngoingReplications() { From 183006f4ce2d4423883a75e450e3c8a4553c5b92 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 2 Aug 2022 11:45:51 +0530 Subject: [PATCH 019/187] Add Executor to decommission node attribute Signed-off-by: Rishab Nahata --- ...NodeAttributeClusterStateTaskExecutor.java | 141 ++++++++++++++ .../decommission/DecommissionAttribute.java | 108 +++++++++++ ...ttributeClusterStateTaskExecutorTests.java | 178 ++++++++++++++++++ 3 files changed, 427 insertions(+) create mode 100644 server/src/main/java/org/opensearch/cluster/coordination/DecommissionNodeAttributeClusterStateTaskExecutor.java create mode 100644 server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java create mode 100644 server/src/test/java/org/opensearch/cluster/coordination/DecommissionNodeAttributeClusterStateTaskExecutorTests.java diff --git a/server/src/main/java/org/opensearch/cluster/coordination/DecommissionNodeAttributeClusterStateTaskExecutor.java b/server/src/main/java/org/opensearch/cluster/coordination/DecommissionNodeAttributeClusterStateTaskExecutor.java new file mode 100644 index 0000000000000..d71cd98d5f25e --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/coordination/DecommissionNodeAttributeClusterStateTaskExecutor.java @@ -0,0 +1,141 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.coordination; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.message.ParameterizedMessage; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.ClusterStateTaskExecutor; +import org.opensearch.cluster.ClusterStateTaskListener; +import org.opensearch.cluster.decommission.DecommissionAttribute; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.node.DiscoveryNodes; +import org.opensearch.cluster.routing.allocation.AllocationService; +import org.opensearch.persistent.PersistentTasksCustomMetadata; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.function.Predicate; + +/** + * Decommissions and shuts down nodes having a given attribute and updates the cluster state + * + * @opensearch.internal + */ +public class DecommissionNodeAttributeClusterStateTaskExecutor + implements + ClusterStateTaskExecutor, + ClusterStateTaskListener { + + private final AllocationService allocationService; + private final Logger logger; + + /** + * Task for the executor. + * + * @opensearch.internal + */ + public static class Task { + + private final DecommissionAttribute decommissionAttribute; + private final String reason; + + public Task(final DecommissionAttribute decommissionAttribute, final String reason) { + this.decommissionAttribute = decommissionAttribute; + this.reason = reason; + } + + public DecommissionAttribute decommissionAttribute() { + return decommissionAttribute; + } + + public String reason() { + return reason; + } + + @Override + public String toString() { + return "Decommission Node Attribute Task{" + + "decommissionAttribute=" + + decommissionAttribute + + ", reason='" + + reason + + '\'' + + '}'; + } + } + + public DecommissionNodeAttributeClusterStateTaskExecutor(final AllocationService allocationService, final Logger logger) { + this.allocationService = allocationService; + this.logger = logger; + } + + @Override + public ClusterTasksResult execute(ClusterState currentState, List tasks) throws Exception { + final DiscoveryNodes.Builder remainingNodesBuilder = DiscoveryNodes.builder(currentState.nodes()); + List nodesToBeRemoved = new ArrayList(); + for (final Task task : tasks) { + final Predicate shouldRemoveNodePredicate = discoveryNode -> nodeHasDecommissionedAttribute(discoveryNode, task); + Iterator nodesIter = currentState.nodes().getNodes().valuesIt(); + while (nodesIter.hasNext()) { + final DiscoveryNode node = nodesIter.next(); + if (shouldRemoveNodePredicate.test(node) && currentState.nodes().nodeExists(node)) { + nodesToBeRemoved.add(node); + } + } + } + if (nodesToBeRemoved.size() <= 0) { + // no nodes to remove, will keep the current cluster state + return ClusterTasksResult.builder() + .successes(tasks) + .build(currentState); + } + for (DiscoveryNode nodeToBeRemoved : nodesToBeRemoved) { + remainingNodesBuilder.remove(nodeToBeRemoved); + } + + final ClusterState remainingNodesClusterState = remainingNodesClusterState(currentState, remainingNodesBuilder); + + return getTaskClusterTasksResult(currentState, tasks, remainingNodesClusterState); + } + + private boolean nodeHasDecommissionedAttribute(DiscoveryNode discoveryNode, Task task) { + String discoveryNodeAttributeValue = discoveryNode.getAttributes().get(task.decommissionAttribute().attributeName()); + return discoveryNodeAttributeValue != null && task.decommissionAttribute().attributeValues().contains(discoveryNodeAttributeValue); + } + + // visible for testing + // hook is used in testing to ensure that correct cluster state is used to test whether a + // rejoin or reroute is needed + protected ClusterState remainingNodesClusterState(final ClusterState currentState, DiscoveryNodes.Builder remainingNodesBuilder) { + return ClusterState.builder(currentState).nodes(remainingNodesBuilder).build(); + } + + protected ClusterTasksResult getTaskClusterTasksResult( + ClusterState currentState, + List tasks, + ClusterState remainingNodesClusterState + ) { + ClusterState ptasksDisassociatedState = PersistentTasksCustomMetadata.disassociateDeadNodes(remainingNodesClusterState); + final ClusterTasksResult.Builder resultBuilder = ClusterTasksResult.< + DecommissionNodeAttributeClusterStateTaskExecutor.Task>builder().successes(tasks); + return resultBuilder.build(allocationService.disassociateDeadNodes(ptasksDisassociatedState, true, describeTasks(tasks))); + } + + @Override + public void onFailure(final String source, final Exception e) { + logger.error(() -> new ParameterizedMessage("unexpected failure during [{}]", source), e); + } + + @Override + public void onNoLongerClusterManager(String source) { + logger.debug("no longer cluster-manager while decommissioning node attribute [{}]", source); + } +} diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java new file mode 100644 index 0000000000000..6260af2823687 --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java @@ -0,0 +1,108 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.decommission; + +import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.common.io.stream.Writeable; + +import java.io.IOException; +import java.util.List; +import java.util.Objects; + +public final class DecommissionAttribute implements Writeable { + private final String attributeName; + private final List attributeValues; + + /** + * Update the attribute values for a given attribute name to decommission + * + * @param decommissionAttribute current decommissioned attribute object + * @param attributeValues values to be updated with + */ + public DecommissionAttribute(DecommissionAttribute decommissionAttribute, List attributeValues) { + this(decommissionAttribute.attributeName, attributeValues); + } + + /** + * Constructs new decommission attribute name values pair + * + * @param attributeName attribute name + * @param attributeValues attribute values + */ + public DecommissionAttribute(String attributeName, List attributeValues) { + this.attributeName = attributeName; + this.attributeValues = attributeValues; + } + + /** + * Returns attribute name + * + * @return attributeName + */ + public String attributeName() { + return this.attributeName; + } + + /** + * Returns attribute values + * + * @return attributeValues + */ + public List attributeValues() { + return this.attributeValues; + } + + public DecommissionAttribute(StreamInput in) throws IOException { + attributeName = in.readString(); + attributeValues = in.readStringList(); + } + + /** + * Writes decommission attribute name values to stream output + * + * @param out stream output + */ + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeString(attributeName); + out.writeStringCollection(attributeValues); + } + + /** + * Checks if this instance is equal to the other instance in attributeName other than {@link #attributeValues}. + * + * @param other other decommission attribute name values + * @return {@code true} if both instances equal in attributeName fields but the attributeValues fields + */ + public boolean equalsIgnoreValues(DecommissionAttribute other) { + return attributeName.equals(other.attributeName); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + DecommissionAttribute that = (DecommissionAttribute) o; + + if (!attributeName.equals(that.attributeName)) return false; + return attributeValues.equals(that.attributeValues); + } + + @Override + public int hashCode() { + return Objects.hash(attributeName, attributeValues); + } + + @Override + public String toString() { + return "DecommissionAttribute{" + attributeName + "}{" + attributeValues().toString() + "}"; + } +} diff --git a/server/src/test/java/org/opensearch/cluster/coordination/DecommissionNodeAttributeClusterStateTaskExecutorTests.java b/server/src/test/java/org/opensearch/cluster/coordination/DecommissionNodeAttributeClusterStateTaskExecutorTests.java new file mode 100644 index 0000000000000..204d31f18e2cf --- /dev/null +++ b/server/src/test/java/org/opensearch/cluster/coordination/DecommissionNodeAttributeClusterStateTaskExecutorTests.java @@ -0,0 +1,178 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.coordination; + +import org.opensearch.Version; +import org.opensearch.cluster.ClusterName; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.ClusterStateTaskExecutor; +import org.opensearch.cluster.decommission.DecommissionAttribute; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.node.DiscoveryNodeRole; +import org.opensearch.cluster.node.DiscoveryNodes; +import org.opensearch.cluster.routing.allocation.AllocationService; +import org.opensearch.test.OpenSearchTestCase; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; + +import static java.util.Collections.singletonMap; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class DecommissionNodeAttributeClusterStateTaskExecutorTests extends OpenSearchTestCase { + + public void testRemoveNodesForDecommissionedAttribute() throws Exception { + final AllocationService allocationService = mock(AllocationService.class); + when(allocationService.disassociateDeadNodes(any(ClusterState.class), eq(true), any(String.class))).thenAnswer( + im -> im.getArguments()[0] + ); + final AtomicReference remainingNodesClusterState = new AtomicReference<>(); + ClusterState clusterState = ClusterState.builder(new ClusterName("test")).build(); + + logger.info("--> adding five nodes on same zone_1"); + clusterState = addNodes(clusterState, "zone_1", "node1", "node2", "node3", "node4", "node5"); + + logger.info("--> adding five nodes on same zone_2"); + clusterState = addNodes(clusterState, "zone_2", "node6", "node7", "node8", "node9", "node10"); + + logger.info("--> adding five nodes on same zone_3"); + clusterState = addNodes(clusterState, "zone_3", "node11", "node12", "node13", "node14", "node15"); + + final DecommissionNodeAttributeClusterStateTaskExecutor executor = new DecommissionNodeAttributeClusterStateTaskExecutor( + allocationService, + logger + ) { + @Override + protected ClusterState remainingNodesClusterState(ClusterState currentState, DiscoveryNodes.Builder remainingNodesBuilder) { + remainingNodesClusterState.set(super.remainingNodesClusterState(currentState, remainingNodesBuilder)); + return remainingNodesClusterState.get(); + } + }; + + final List tasks = new ArrayList<>(); + tasks.add( + new DecommissionNodeAttributeClusterStateTaskExecutor.Task( + new DecommissionAttribute("zone", Collections.singletonList("zone_3")), + "unit test zone decommission executor" + ) + ); + + final ClusterStateTaskExecutor.ClusterTasksResult result = executor.execute( + clusterState, + tasks + ); + + ClusterState expectedClusterState = remainingNodesClusterState.get(); + ClusterState actualClusterState = result.resultingState; + + // Assert cluster state is updated and is successful + verify(allocationService).disassociateDeadNodes(eq(expectedClusterState), eq(true), any(String.class)); + assertEquals(actualClusterState, expectedClusterState); + assertTrue(result.executionResults.get(tasks.get(0)).isSuccess()); + + // Verify only 10 nodes present in the cluster after decommissioning + assertEquals(actualClusterState.nodes().getNodes().size(), 10); + + // Verify no nodes has attribute (zone, zone_3) + Iterator currDiscoveryNodeIterator = actualClusterState.nodes().getNodes().valuesIt(); + while (currDiscoveryNodeIterator.hasNext()) { + final DiscoveryNode node = currDiscoveryNodeIterator.next(); + assertNotEquals(node.getAttributes().get("zone"), "zone_3"); + } + } + + public void testSameClusterStateAfterExecutionForUnknownAttributeNameAndValue() throws Exception { + final AllocationService allocationService = mock(AllocationService.class); + when(allocationService.disassociateDeadNodes(any(ClusterState.class), eq(true), any(String.class))).thenAnswer( + im -> im.getArguments()[0] + ); + final AtomicReference remainingNodesClusterState = new AtomicReference<>(); + ClusterState clusterState = ClusterState.builder(new ClusterName("test")).build(); + + logger.info("--> adding five nodes on same zone_1"); + clusterState = addNodes(clusterState, "zone_1", "node1", "node2", "node3", "node4", "node5"); + + logger.info("--> adding five nodes on same zone_2"); + clusterState = addNodes(clusterState, "zone_2", "node6", "node7", "node8", "node9", "node10"); + + logger.info("--> adding five nodes on same zone_3"); + clusterState = addNodes(clusterState, "zone_3", "node11", "node12", "node13", "node14", "node15"); + + final DecommissionNodeAttributeClusterStateTaskExecutor executor = new DecommissionNodeAttributeClusterStateTaskExecutor( + allocationService, + logger + ) { + @Override + protected ClusterState remainingNodesClusterState(ClusterState currentState, DiscoveryNodes.Builder remainingNodesBuilder) { + remainingNodesClusterState.set(super.remainingNodesClusterState(currentState, remainingNodesBuilder)); + return remainingNodesClusterState.get(); + } + }; + + final List tasks = new ArrayList<>(); + // Task 1 with unknown attribute name + tasks.add( + new DecommissionNodeAttributeClusterStateTaskExecutor.Task( + new DecommissionAttribute("unknown_zone_name", Collections.singletonList("unknown_zone_value")), + "unit test zone decommission executor" + ) + ); + // Task 2 with unknown attribute value + tasks.add( + new DecommissionNodeAttributeClusterStateTaskExecutor.Task( + new DecommissionAttribute("zone", Collections.singletonList("unknown_zone_value")), + "unit test zone decommission executor" + ) + ); + + final ClusterStateTaskExecutor.ClusterTasksResult result = executor.execute( + clusterState, + tasks + ); + + ClusterState expectedClusterState = remainingNodesClusterState.get(); + ClusterState actualClusterState = result.resultingState; + + // assert that disassociate dead node tasks is never executed + verify(allocationService, never()).disassociateDeadNodes(eq(expectedClusterState), eq(true), any(String.class)); + + // assert that cluster state remains same + assertEquals(clusterState, actualClusterState); + + // Verify all 15 nodes present in the cluster after decommissioning unknown attribute name + assertEquals(actualClusterState.nodes().getNodes().size(), 15); + } + + private ClusterState addNodes(ClusterState clusterState, String zone, String... nodeIds) { + DiscoveryNodes.Builder nodeBuilder = DiscoveryNodes.builder(clusterState.nodes()); + org.opensearch.common.collect.List.of(nodeIds).forEach(nodeId -> nodeBuilder.add(newNode(nodeId, singletonMap("zone", zone)))); + clusterState = ClusterState.builder(clusterState).nodes(nodeBuilder).build(); + return clusterState; + } + + private DiscoveryNode newNode(String nodeId, Map attributes) { + return new DiscoveryNode(nodeId, buildNewFakeTransportAddress(), attributes, CLUSTER_MANAGER_DATA_ROLES, Version.CURRENT); + } + + final private static Set CLUSTER_MANAGER_DATA_ROLES = Collections.unmodifiableSet( + new HashSet<>(Arrays.asList(DiscoveryNodeRole.CLUSTER_MANAGER_ROLE, DiscoveryNodeRole.DATA_ROLE)) + ); +} From 042375bf623f2394482e8eec3964e223aab5e272 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Mon, 8 Aug 2022 20:05:20 +0530 Subject: [PATCH 020/187] Add DecommissionHelper Signed-off-by: Rishab Nahata --- .../decommission/DecommissionHelper.java | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java new file mode 100644 index 0000000000000..6b9e480abcef7 --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java @@ -0,0 +1,58 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.decommission; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.ClusterStateTaskConfig; +import org.opensearch.cluster.ClusterStateTaskListener; +import org.opensearch.cluster.coordination.NodeRemovalClusterStateTaskExecutor; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.node.DiscoveryNodes; +import org.opensearch.cluster.service.ClusterManagerService; +import org.opensearch.common.Priority; +import org.opensearch.common.inject.Inject; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class DecommissionHelper { + + private static final Logger logger = LogManager.getLogger(DecommissionHelper.class); + + private final NodeRemovalClusterStateTaskExecutor nodeRemovalExecutor; + private final ClusterManagerService clusterManagerService; + + DecommissionHelper( + ClusterManagerService clusterManagerService, + NodeRemovalClusterStateTaskExecutor nodeRemovalClusterStateTaskExecutor + ) { + this.nodeRemovalExecutor = nodeRemovalClusterStateTaskExecutor; + this.clusterManagerService = clusterManagerService; + } + + private void handleNodesDecommissionRequest(List nodesToBeDecommissioned, String reason) { + final Map nodesDecommissionTasks = new LinkedHashMap<>(); + nodesToBeDecommissioned.forEach(discoveryNode -> { + final NodeRemovalClusterStateTaskExecutor.Task task = new NodeRemovalClusterStateTaskExecutor.Task( + discoveryNode, reason + ); + nodesDecommissionTasks.put(task, nodeRemovalExecutor); + }); + final String source = "node-decommissioned"; + clusterManagerService.submitStateUpdateTasks( + source, + nodesDecommissionTasks, + ClusterStateTaskConfig.build(Priority.IMMEDIATE), + nodeRemovalExecutor + ); + } +} From 50de6084743882fa1ac9b6be27a313ba5eba9f68 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Wed, 17 Aug 2022 15:46:09 +0530 Subject: [PATCH 021/187] Decommission service implementation with metadata Signed-off-by: Rishab Nahata --- .../org/opensearch/OpenSearchException.java | 7 + .../org/opensearch/cluster/ClusterModule.java | 9 + ...NodeAttributeClusterStateTaskExecutor.java | 141 ---------- .../decommission/DecommissionAttribute.java | 47 ++-- .../DecommissionFailedException.java | 49 ++++ .../decommission/DecommissionHelper.java | 15 +- .../decommission/DecommissionService.java | 223 +++++++++++++++ .../decommission/DecommissionStatus.java | 94 +++++++ .../DecommissionAttributeMetadata.java | 254 ++++++++++++++++++ ...ttributeClusterStateTaskExecutorTests.java | 178 ------------ 10 files changed, 664 insertions(+), 353 deletions(-) delete mode 100644 server/src/main/java/org/opensearch/cluster/coordination/DecommissionNodeAttributeClusterStateTaskExecutor.java create mode 100644 server/src/main/java/org/opensearch/cluster/decommission/DecommissionFailedException.java create mode 100644 server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java create mode 100644 server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java create mode 100644 server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java delete mode 100644 server/src/test/java/org/opensearch/cluster/coordination/DecommissionNodeAttributeClusterStateTaskExecutorTests.java diff --git a/server/src/main/java/org/opensearch/OpenSearchException.java b/server/src/main/java/org/opensearch/OpenSearchException.java index 87efc03734d26..d3e1bef9b6dbb 100644 --- a/server/src/main/java/org/opensearch/OpenSearchException.java +++ b/server/src/main/java/org/opensearch/OpenSearchException.java @@ -34,6 +34,7 @@ import org.opensearch.action.support.replication.ReplicationOperation; import org.opensearch.cluster.action.shard.ShardStateAction; +import org.opensearch.cluster.decommission.DecommissionFailedException; import org.opensearch.common.CheckedFunction; import org.opensearch.common.Nullable; import org.opensearch.common.ParseField; @@ -1608,6 +1609,12 @@ private enum OpenSearchExceptionHandle { org.opensearch.index.shard.PrimaryShardClosedException::new, 162, V_3_0_0 + ), + DECOMMISSION_FAILED_EXCEPTION( + org.opensearch.cluster.decommission.DecommissionFailedException.class, + org.opensearch.cluster.decommission.DecommissionFailedException::new, + 163, + V_2_1_0 ); final Class exceptionClass; diff --git a/server/src/main/java/org/opensearch/cluster/ClusterModule.java b/server/src/main/java/org/opensearch/cluster/ClusterModule.java index f8ba520e465e2..de63369dafc89 100644 --- a/server/src/main/java/org/opensearch/cluster/ClusterModule.java +++ b/server/src/main/java/org/opensearch/cluster/ClusterModule.java @@ -38,6 +38,7 @@ import org.opensearch.cluster.metadata.ComponentTemplateMetadata; import org.opensearch.cluster.metadata.ComposableIndexTemplateMetadata; import org.opensearch.cluster.metadata.DataStreamMetadata; +import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; import org.opensearch.cluster.metadata.IndexGraveyard; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.metadata.Metadata; @@ -191,6 +192,7 @@ public static List getNamedWriteables() { ComposableIndexTemplateMetadata::readDiffFrom ); registerMetadataCustom(entries, DataStreamMetadata.TYPE, DataStreamMetadata::new, DataStreamMetadata::readDiffFrom); + registerMetadataCustom(entries, DecommissionAttributeMetadata.TYPE, DecommissionAttributeMetadata::new, DecommissionAttributeMetadata::readDiffFrom); // Task Status (not Diffable) entries.add(new Entry(Task.Status.class, PersistentTasksNodeService.Status.NAME, PersistentTasksNodeService.Status::new)); return entries; @@ -274,6 +276,13 @@ public static List getNamedXWriteables() { DataStreamMetadata::fromXContent ) ); + entries.add( + new NamedXContentRegistry.Entry( + Metadata.Custom.class, + new ParseField(DecommissionAttributeMetadata.TYPE), + DecommissionAttributeMetadata::fromXContent + ) + ); return entries; } diff --git a/server/src/main/java/org/opensearch/cluster/coordination/DecommissionNodeAttributeClusterStateTaskExecutor.java b/server/src/main/java/org/opensearch/cluster/coordination/DecommissionNodeAttributeClusterStateTaskExecutor.java deleted file mode 100644 index d71cd98d5f25e..0000000000000 --- a/server/src/main/java/org/opensearch/cluster/coordination/DecommissionNodeAttributeClusterStateTaskExecutor.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -package org.opensearch.cluster.coordination; - -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.message.ParameterizedMessage; -import org.opensearch.cluster.ClusterState; -import org.opensearch.cluster.ClusterStateTaskExecutor; -import org.opensearch.cluster.ClusterStateTaskListener; -import org.opensearch.cluster.decommission.DecommissionAttribute; -import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.cluster.node.DiscoveryNodes; -import org.opensearch.cluster.routing.allocation.AllocationService; -import org.opensearch.persistent.PersistentTasksCustomMetadata; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.function.Predicate; - -/** - * Decommissions and shuts down nodes having a given attribute and updates the cluster state - * - * @opensearch.internal - */ -public class DecommissionNodeAttributeClusterStateTaskExecutor - implements - ClusterStateTaskExecutor, - ClusterStateTaskListener { - - private final AllocationService allocationService; - private final Logger logger; - - /** - * Task for the executor. - * - * @opensearch.internal - */ - public static class Task { - - private final DecommissionAttribute decommissionAttribute; - private final String reason; - - public Task(final DecommissionAttribute decommissionAttribute, final String reason) { - this.decommissionAttribute = decommissionAttribute; - this.reason = reason; - } - - public DecommissionAttribute decommissionAttribute() { - return decommissionAttribute; - } - - public String reason() { - return reason; - } - - @Override - public String toString() { - return "Decommission Node Attribute Task{" - + "decommissionAttribute=" - + decommissionAttribute - + ", reason='" - + reason - + '\'' - + '}'; - } - } - - public DecommissionNodeAttributeClusterStateTaskExecutor(final AllocationService allocationService, final Logger logger) { - this.allocationService = allocationService; - this.logger = logger; - } - - @Override - public ClusterTasksResult execute(ClusterState currentState, List tasks) throws Exception { - final DiscoveryNodes.Builder remainingNodesBuilder = DiscoveryNodes.builder(currentState.nodes()); - List nodesToBeRemoved = new ArrayList(); - for (final Task task : tasks) { - final Predicate shouldRemoveNodePredicate = discoveryNode -> nodeHasDecommissionedAttribute(discoveryNode, task); - Iterator nodesIter = currentState.nodes().getNodes().valuesIt(); - while (nodesIter.hasNext()) { - final DiscoveryNode node = nodesIter.next(); - if (shouldRemoveNodePredicate.test(node) && currentState.nodes().nodeExists(node)) { - nodesToBeRemoved.add(node); - } - } - } - if (nodesToBeRemoved.size() <= 0) { - // no nodes to remove, will keep the current cluster state - return ClusterTasksResult.builder() - .successes(tasks) - .build(currentState); - } - for (DiscoveryNode nodeToBeRemoved : nodesToBeRemoved) { - remainingNodesBuilder.remove(nodeToBeRemoved); - } - - final ClusterState remainingNodesClusterState = remainingNodesClusterState(currentState, remainingNodesBuilder); - - return getTaskClusterTasksResult(currentState, tasks, remainingNodesClusterState); - } - - private boolean nodeHasDecommissionedAttribute(DiscoveryNode discoveryNode, Task task) { - String discoveryNodeAttributeValue = discoveryNode.getAttributes().get(task.decommissionAttribute().attributeName()); - return discoveryNodeAttributeValue != null && task.decommissionAttribute().attributeValues().contains(discoveryNodeAttributeValue); - } - - // visible for testing - // hook is used in testing to ensure that correct cluster state is used to test whether a - // rejoin or reroute is needed - protected ClusterState remainingNodesClusterState(final ClusterState currentState, DiscoveryNodes.Builder remainingNodesBuilder) { - return ClusterState.builder(currentState).nodes(remainingNodesBuilder).build(); - } - - protected ClusterTasksResult getTaskClusterTasksResult( - ClusterState currentState, - List tasks, - ClusterState remainingNodesClusterState - ) { - ClusterState ptasksDisassociatedState = PersistentTasksCustomMetadata.disassociateDeadNodes(remainingNodesClusterState); - final ClusterTasksResult.Builder resultBuilder = ClusterTasksResult.< - DecommissionNodeAttributeClusterStateTaskExecutor.Task>builder().successes(tasks); - return resultBuilder.build(allocationService.disassociateDeadNodes(ptasksDisassociatedState, true, describeTasks(tasks))); - } - - @Override - public void onFailure(final String source, final Exception e) { - logger.error(() -> new ParameterizedMessage("unexpected failure during [{}]", source), e); - } - - @Override - public void onNoLongerClusterManager(String source) { - logger.debug("no longer cluster-manager while decommissioning node attribute [{}]", source); - } -} diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java index 6260af2823687..db4e06e854518 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java @@ -18,27 +18,27 @@ public final class DecommissionAttribute implements Writeable { private final String attributeName; - private final List attributeValues; + private final String attributeValue; /** - * Update the attribute values for a given attribute name to decommission + * Update the attribute value for a given attribute name to decommission * * @param decommissionAttribute current decommissioned attribute object - * @param attributeValues values to be updated with + * @param attributeValue attribute value to be updated with */ - public DecommissionAttribute(DecommissionAttribute decommissionAttribute, List attributeValues) { - this(decommissionAttribute.attributeName, attributeValues); + public DecommissionAttribute(DecommissionAttribute decommissionAttribute, String attributeValue) { + this(decommissionAttribute.attributeName, attributeValue); } /** - * Constructs new decommission attribute name values pair + * Constructs new decommission attribute name value pair * * @param attributeName attribute name - * @param attributeValues attribute values + * @param attributeValue attribute value */ - public DecommissionAttribute(String attributeName, List attributeValues) { + public DecommissionAttribute(String attributeName, String attributeValue) { this.attributeName = attributeName; - this.attributeValues = attributeValues; + this.attributeValue = attributeValue; } /** @@ -51,35 +51,35 @@ public String attributeName() { } /** - * Returns attribute values + * Returns attribute value * - * @return attributeValues + * @return attributeValue */ - public List attributeValues() { - return this.attributeValues; + public String attributeValue() { + return this.attributeValue; } public DecommissionAttribute(StreamInput in) throws IOException { attributeName = in.readString(); - attributeValues = in.readStringList(); + attributeValue = in.readString(); } /** - * Writes decommission attribute name values to stream output + * Writes decommission attribute name value to stream output * * @param out stream output */ @Override public void writeTo(StreamOutput out) throws IOException { out.writeString(attributeName); - out.writeStringCollection(attributeValues); + out.writeString(attributeValue); } /** - * Checks if this instance is equal to the other instance in attributeName other than {@link #attributeValues}. + * Checks if this instance is equal to the other instance in attributeName but differ in attribute value {@link #attributeValue}. * - * @param other other decommission attribute name values - * @return {@code true} if both instances equal in attributeName fields but the attributeValues fields + * @param other other decommission attribute name value + * @return {@code true} if both instances equal in attributeName fields but the attributeValue field */ public boolean equalsIgnoreValues(DecommissionAttribute other) { return attributeName.equals(other.attributeName); @@ -93,16 +93,13 @@ public boolean equals(Object o) { DecommissionAttribute that = (DecommissionAttribute) o; if (!attributeName.equals(that.attributeName)) return false; - return attributeValues.equals(that.attributeValues); + return attributeValue.equals(that.attributeValue); } @Override public int hashCode() { - return Objects.hash(attributeName, attributeValues); + return Objects.hash(attributeName, attributeValue); } - @Override - public String toString() { - return "DecommissionAttribute{" + attributeName + "}{" + attributeValues().toString() + "}"; - } + } diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionFailedException.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionFailedException.java new file mode 100644 index 0000000000000..3a611c2488779 --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionFailedException.java @@ -0,0 +1,49 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.decommission; + +import org.opensearch.OpenSearchException; +import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.common.io.stream.StreamOutput; + +import java.io.IOException; + +public class DecommissionFailedException extends OpenSearchException { + + private final DecommissionAttribute decommissionAttribute; + + public DecommissionFailedException(DecommissionAttribute decommissionAttribute, String msg) { + this(decommissionAttribute, msg, null); + } + + public DecommissionFailedException(DecommissionAttribute decommissionAttribute, String msg, Throwable cause) { + super("[" + (decommissionAttribute == null ? "_na" : decommissionAttribute.toString()) + "] " + msg, cause); + this.decommissionAttribute = decommissionAttribute; + } + + public DecommissionFailedException(StreamInput in) throws IOException { + super(in); + decommissionAttribute = new DecommissionAttribute(in); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + decommissionAttribute.writeTo(out); + } + + /** + * Returns decommission attribute + * + * @return decommission attribute + */ + public DecommissionAttribute decommissionAttribute() { + return decommissionAttribute; + } +} diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java index 6b9e480abcef7..d1eb17adc9747 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java @@ -10,15 +10,12 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateTaskConfig; import org.opensearch.cluster.ClusterStateTaskListener; import org.opensearch.cluster.coordination.NodeRemovalClusterStateTaskExecutor; import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.cluster.node.DiscoveryNodes; -import org.opensearch.cluster.service.ClusterManagerService; +import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Priority; -import org.opensearch.common.inject.Inject; import java.util.LinkedHashMap; import java.util.List; @@ -29,17 +26,17 @@ public class DecommissionHelper { private static final Logger logger = LogManager.getLogger(DecommissionHelper.class); private final NodeRemovalClusterStateTaskExecutor nodeRemovalExecutor; - private final ClusterManagerService clusterManagerService; + private final ClusterService clusterService; DecommissionHelper( - ClusterManagerService clusterManagerService, + ClusterService clusterService, NodeRemovalClusterStateTaskExecutor nodeRemovalClusterStateTaskExecutor ) { this.nodeRemovalExecutor = nodeRemovalClusterStateTaskExecutor; - this.clusterManagerService = clusterManagerService; + this.clusterService = clusterService; } - private void handleNodesDecommissionRequest(List nodesToBeDecommissioned, String reason) { + public void handleNodesDecommissionRequest(List nodesToBeDecommissioned, String reason) { final Map nodesDecommissionTasks = new LinkedHashMap<>(); nodesToBeDecommissioned.forEach(discoveryNode -> { final NodeRemovalClusterStateTaskExecutor.Task task = new NodeRemovalClusterStateTaskExecutor.Task( @@ -48,7 +45,7 @@ private void handleNodesDecommissionRequest(List nodesToBeDecommi nodesDecommissionTasks.put(task, nodeRemovalExecutor); }); final String source = "node-decommissioned"; - clusterManagerService.submitStateUpdateTasks( + clusterService.submitStateUpdateTasks( source, nodesDecommissionTasks, ClusterStateTaskConfig.build(Priority.IMMEDIATE), diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java new file mode 100644 index 0000000000000..160b729b14b3a --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -0,0 +1,223 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.decommission; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.message.ParameterizedMessage; +import org.opensearch.action.ActionListener; +import org.opensearch.cluster.ClusterChangedEvent; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.ClusterStateApplier; +import org.opensearch.cluster.ClusterStateUpdateTask; +import org.opensearch.cluster.ack.ClusterStateUpdateResponse; +import org.opensearch.cluster.coordination.NodeRemovalClusterStateTaskExecutor; +import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; +import org.opensearch.cluster.metadata.Metadata; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.routing.allocation.AllocationService; +import org.opensearch.cluster.routing.allocation.decider.AwarenessAllocationDecider; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.Priority; +import org.opensearch.common.inject.Inject; +import org.opensearch.common.settings.ClusterSettings; +import org.opensearch.common.settings.Settings; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportService; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.function.Predicate; + +// do we need to implement ClusterStateApplier -> will a change in cluster state impact this service?? +public class DecommissionService implements ClusterStateApplier { + + private static final Logger logger = LogManager.getLogger(DecommissionService.class); + + private final ClusterService clusterService; + private final TransportService transportService; + private final NodeRemovalClusterStateTaskExecutor nodeRemovalExecutor; + private final ThreadPool threadPool; + private final DecommissionHelper decommissionHelper; + private ClusterState clusterState; + private volatile List awarenessAttributes; + + @Inject + public DecommissionService( + Settings settings, + ClusterSettings clusterSettings, + ClusterService clusterService, + TransportService transportService, + ThreadPool threadPool, + AllocationService allocationService + ) { + this.clusterService = clusterService; + this.transportService = transportService; + this.threadPool = threadPool; + this.clusterState = clusterService.state(); // TODO - check if this is the right way + this.nodeRemovalExecutor = new NodeRemovalClusterStateTaskExecutor(allocationService, logger); + this.decommissionHelper = new DecommissionHelper( + clusterService, + nodeRemovalExecutor + ); + this.awarenessAttributes = AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING.get(settings); + clusterSettings.addSettingsUpdateConsumer( + AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING, + this::setAwarenessAttributes + ); + } + + List getAwarenessAttributes() { + return awarenessAttributes; + } + + private void setAwarenessAttributes(List awarenessAttributes) { + this.awarenessAttributes = awarenessAttributes; + } + + public void initiateAttributeDecommissioning( + final DecommissionAttribute decommissionAttribute, + final ActionListener listener + ) { + /** + * 1. Abdicate master + * 2. Register attribute -> status should be set to INIT + * 3. Trigger weigh away for graceful decommission -> status should be set to DECOMMISSIONING + * 4. Once zone is weighed away -> trigger zone decommission using executor -> status should be set to DECOMMISSIONED on successful response + * 5. Clear voting config + */ + registerDecommissionAttribute(decommissionAttribute, listener); + } + + /** + * Registers new decommissioned attribute metadata in the cluster state + *

+ * This method can be only called on the cluster-manager node. It tries to create a new decommissioned attribute on the master + * and if it was successful it adds new decommissioned attribute to cluster metadata. + *

+ * This method should only be called once the eligible cluster manager node having decommissioned attribute is abdicated + * + * @param decommissionAttribute register decommission attribute in the metadata request + * @param listener register decommission listener + */ + private void registerDecommissionAttribute( + final DecommissionAttribute decommissionAttribute, + final ActionListener listener + ) { + validateAwarenessAttribute(decommissionAttribute, getAwarenessAttributes()); + clusterService.submitStateUpdateTask( + "put_decommission [" + decommissionAttribute + "]", + new ClusterStateUpdateTask(Priority.URGENT) { + @Override + public ClusterState execute(ClusterState currentState) throws Exception { + logger.info("decommission request for attribute [{}] received", decommissionAttribute.toString()); + Metadata metadata = currentState.metadata(); + Metadata.Builder mdBuilder = Metadata.builder(metadata); + DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.custom(DecommissionAttributeMetadata.TYPE); + ensureNoAwarenessAttributeDecommissioned(decommissionAttributeMetadata, decommissionAttribute); + decommissionAttributeMetadata = new DecommissionAttributeMetadata(decommissionAttribute); + mdBuilder.putCustom(DecommissionAttributeMetadata.TYPE, decommissionAttributeMetadata); + return ClusterState.builder(currentState).metadata(mdBuilder).build(); + } + + @Override + public void onFailure(String source, Exception e) { + // TODO - should we put the weights back to zone, since we weighed away the zone before we started registering the metadata + // TODO - should we modify logic of logging for ease of debugging? + if (e instanceof DecommissionFailedException) { + logger.error(() -> new ParameterizedMessage("failed to decommission attribute [{}]", decommissionAttribute.toString()), e); + } else { + clusterService.submitStateUpdateTask( + "decommission_failed", + new ClusterStateUpdateTask(Priority.URGENT) { + @Override + public ClusterState execute(ClusterState currentState) throws Exception { + Metadata metadata = currentState.metadata(); + Metadata.Builder mdBuilder = Metadata.builder(metadata); + logger.info("decommission request for attribute [{}] failed", decommissionAttribute.toString()); + DecommissionAttributeMetadata decommissionAttributeMetadata = new DecommissionAttributeMetadata( + decommissionAttribute, + DecommissionStatus.DECOMMISSION_FAILED + ); + mdBuilder.putCustom(DecommissionAttributeMetadata.TYPE, decommissionAttributeMetadata); + return ClusterState.builder(currentState).metadata(mdBuilder).build(); + } + + @Override + public void onFailure(String source, Exception e) { + logger.error(() -> new ParameterizedMessage( + "failed to mark status as DECOMMISSION_FAILED for decommission attribute [{}]", + decommissionAttribute.toString()), e); +// listener.onFailure(e); + } + } + ); + } + listener.onFailure(e); + } + + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + if (!newState.equals(oldState)) { + // TODO - drain the nodes before decommissioning + failDecommissionedNodes(newState); + listener.onResponse(new ClusterStateUpdateResponse(true)); + } + listener.onResponse(new ClusterStateUpdateResponse(false)); + } + } + ); + } + + private static void validateAwarenessAttribute(final DecommissionAttribute decommissionAttribute, List awarenessAttributes) { + if (!awarenessAttributes.contains(decommissionAttribute.attributeName())) { + throw new DecommissionFailedException(decommissionAttribute, "invalid awareness attribute requested for decommissioning"); + } + // TODO - should attribute value be part of force zone values? If yes, read setting and throw exception if not found + } + + private static void ensureNoAwarenessAttributeDecommissioned( + DecommissionAttributeMetadata decommissionAttributeMetadata, + DecommissionAttribute decommissionAttribute + ) { + // If the previous decommission request failed, we will allow the request to pass this check + if (decommissionAttributeMetadata != null && !decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_FAILED)) { + throw new DecommissionFailedException(decommissionAttribute, "one awareness attribute already decommissioned, " + + "recommission before triggering another decommission"); + } + } + + private void failDecommissionedNodes(ClusterState state) { + DecommissionAttributeMetadata decommissionAttributeMetadata = state.metadata().custom(DecommissionAttributeMetadata.TYPE); + assert decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSIONING) : "unexpected status encountered while decommissioning nodes"; + DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute(); + List nodesToBeDecommissioned = new ArrayList<>(); + final Predicate shouldRemoveNodePredicate = discoveryNode -> nodeHasDecommissionedAttribute(discoveryNode, decommissionAttribute); + Iterator nodesIter = state.nodes().getNodes().valuesIt(); + while (nodesIter.hasNext()) { + final DiscoveryNode node = nodesIter.next(); + if (shouldRemoveNodePredicate.test(node)) { + nodesToBeDecommissioned.add(node); + } + } + decommissionHelper.handleNodesDecommissionRequest(nodesToBeDecommissioned, "nodes-decommissioned"); + } + + private static boolean nodeHasDecommissionedAttribute(DiscoveryNode discoveryNode, DecommissionAttribute decommissionAttribute) { + return discoveryNode.getAttributes().get( + decommissionAttribute.attributeName() + ).equals(decommissionAttribute.attributeValue()); + } + + @Override + public void applyClusterState(ClusterChangedEvent event) { + clusterState = event.state(); + } +} diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java new file mode 100644 index 0000000000000..8f4ca3a6f578a --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java @@ -0,0 +1,94 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.decommission; + +public enum DecommissionStatus { + /** + * Decommission process is initiated + */ + INIT((byte) 0), + /** + * Decommission process has started, decommissioned nodes should be weighed away + */ + DECOMMISSIONING((byte) 1), + /** + * Decommissioning awareness attribute completed + */ + DECOMMISSIONED((byte) 2), + /** + * Decommission request failed + */ + DECOMMISSION_FAILED((byte) 3), + /** + * Recommission request received, recommissioning process has started + */ + RECOMMISSIONING((byte) 4), + /** + * Recommission request failed. No nodes should fail to join the cluster with decommission exception + */ + RECOMMISSION_FAILED((byte) 5); + + private final byte value; + + DecommissionStatus(byte value) { + this.value = value; + } + + /** + * Returns code that represents the decommission state + * + * @return code for the state + */ + public byte value() { + return value; + } + + /** + * Generate decommission state from code + * + * @param value the state code + * @return state + */ + public static DecommissionStatus fromValue(byte value) { + switch (value) { + case 0: + return INIT; + case 1: + return DECOMMISSIONING; + case 2: + return DECOMMISSIONED; + case 3: + return DECOMMISSION_FAILED; + case 4: + return RECOMMISSIONING; + case 5: + return RECOMMISSION_FAILED; + default: + throw new IllegalArgumentException("No decommission state for value [" + value + "]"); + } + } + + public static DecommissionStatus fromString(String status) { + if ("init".equals(status)) { + return INIT; + } else if ("decommissioning".equals(status)) { + return DECOMMISSIONING; + } else if ("decommissioned".equals(status)) { + return DECOMMISSIONED; + } else if ("decommission_failed".equals(status)) { + return DECOMMISSION_FAILED; + } else if ("recommissioning".equals(status)) { + return RECOMMISSIONING; + } else if ("recommission_failed".equals(status)) { + return RECOMMISSION_FAILED; + } + throw new IllegalStateException("No status match for [" + status + "]"); + } +} + diff --git a/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java b/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java new file mode 100644 index 0000000000000..dd0c78d7519b1 --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java @@ -0,0 +1,254 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.metadata; + +import org.opensearch.OpenSearchParseException; +import org.opensearch.Version; +import org.opensearch.cluster.AbstractNamedDiffable; +import org.opensearch.cluster.NamedDiff; +import org.opensearch.cluster.decommission.DecommissionAttribute; +import org.opensearch.cluster.decommission.DecommissionStatus; +import org.opensearch.cluster.metadata.Metadata.Custom; +import org.opensearch.common.Nullable; +import org.opensearch.common.Strings; +import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.common.xcontent.ToXContent; +import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.xcontent.XContentParser; + +import java.io.IOException; +import java.util.EnumSet; +import java.util.Objects; + +/** + * Contains metadata about decommission attribute + * + * @opensearch.internal + */ +public class DecommissionAttributeMetadata extends AbstractNamedDiffable implements Custom { + + public static final String TYPE = "decommissionedAttribute"; + + private final DecommissionAttribute decommissionAttribute; + private final DecommissionStatus status; + public static final String attributeType = "awareness"; + + /** + * Constructs new decommission attribute metadata with given status + * + * @param decommissionAttribute attribute details + * @param status current status of the attribute decommission + */ + public DecommissionAttributeMetadata(DecommissionAttribute decommissionAttribute, DecommissionStatus status) { + this.decommissionAttribute = decommissionAttribute; + this.status = status; + } + + /** + * Constructs new decommission attribute metadata with status as {@link DecommissionStatus#INIT} + * + * @param decommissionAttribute attribute details + */ + public DecommissionAttributeMetadata(DecommissionAttribute decommissionAttribute) { + this.decommissionAttribute = decommissionAttribute; + this.status = DecommissionStatus.INIT; + } + + /** + * Returns the current decommissioned attribute + * + * @return decommissioned attributes + */ + public DecommissionAttribute decommissionAttribute() { + return this.decommissionAttribute; + } + + /** + * Returns the current status of the attribute decommission + * + * @return attribute type + */ + public DecommissionStatus status() { + return this.status; + } + + public DecommissionAttributeMetadata withUpdatedStatus( + DecommissionAttributeMetadata metadata, + DecommissionStatus status) { + return new DecommissionAttributeMetadata( + metadata.decommissionAttribute(), + status + ); + } + + /** + * Creates a new instance with a updated attribute value. + * + * @param metadata current metadata + * @param attributeValue new attribute value + * @return new instance with updated attribute value and status as DecommissionStatus.INIT + */ + public DecommissionAttributeMetadata withUpdatedAttributeValue( + DecommissionAttributeMetadata metadata, + String attributeValue + ) { + return new DecommissionAttributeMetadata( + new DecommissionAttribute(metadata.decommissionAttribute, attributeValue), + DecommissionStatus.INIT + ); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + DecommissionAttributeMetadata that = (DecommissionAttributeMetadata) o; + + if (!status.equals(that.status)) return false; + return decommissionAttribute.equals(that.decommissionAttribute); + } + + /** + * Checks if this instance and the given instance share the same decommissioned attributeName + * and only differ in the attributeValue {@link DecommissionAttribute#attributeValue()} + * + * @param other other decommission attribute metadata + * @return {@code true} iff both instances contain the same attributeName + */ + public boolean equalsIgnoreValue(@Nullable DecommissionAttributeMetadata other) { + if (other == null) { + return false; + } + if (!status.equals(other.status)) return false; + return decommissionAttribute.equalsIgnoreValues(other.decommissionAttribute); + } + + @Override + public int hashCode() { + return Objects.hash(attributeType, decommissionAttribute, status); + } + + /** + * {@inheritDoc} + */ + @Override + public String getWriteableName() { + return TYPE; + } + + @Override + public Version getMinimalSupportedVersion() { + return Version.CURRENT.minimumCompatibilityVersion(); + } + + public DecommissionAttributeMetadata(StreamInput in) throws IOException { + this.status = DecommissionStatus.fromValue(in.readByte()); + this.decommissionAttribute = new DecommissionAttribute(in); + } + + public static NamedDiff readDiffFrom(StreamInput in) throws IOException { + return readDiffFrom(Custom.class, TYPE, in); + } + + /** + * {@inheritDoc} + */ + @Override + public void writeTo(StreamOutput out) throws IOException { + decommissionAttribute.writeTo(out); + out.writeByte(status.value()); + out.writeString(attributeType); + } + + public static DecommissionAttributeMetadata fromXContent(XContentParser parser) throws IOException { + XContentParser.Token token; + DecommissionAttribute decommissionAttribute = null; + DecommissionStatus status = null; + if ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + String currentFieldName = parser.currentName(); + if (attributeType.equals(currentFieldName)) { + if (parser.nextToken() != XContentParser.Token.START_OBJECT) { + throw new OpenSearchParseException("failed to parse decommission attribute type [{}], expected object", attributeType); + } + token = parser.nextToken(); + if (token != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + String fieldName = parser.currentName(); + String value; + token = parser.nextToken(); + if (token != XContentParser.Token.VALUE_STRING) { + value = parser.text(); + } else { + throw new OpenSearchParseException("failed to parse attribute [{}], expected string for attribute value", fieldName); + } + decommissionAttribute = new DecommissionAttribute(fieldName, value); + } else { + throw new OpenSearchParseException("failed to parse attribute type [{}], unexpected type", attributeType); + } + } else { + throw new OpenSearchParseException("failed to parse attribute type [{}]", attributeType); + } + } else if ("status".equals(currentFieldName)) { + if (parser.nextToken() != XContentParser.Token.VALUE_STRING) { + throw new OpenSearchParseException("failed to parse status of decommissioning, expected string but found unknown type"); + } + status = DecommissionStatus.fromString(parser.text()); + } else { + throw new OpenSearchParseException( + "unknown field found [{}], failed to parse the decommission attribute", + currentFieldName + ); + } + } + } + return new DecommissionAttributeMetadata(decommissionAttribute, status); + } + + /** + * {@inheritDoc} + */ + @Override + public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params) throws IOException { + toXContent(decommissionAttribute, status, attributeType, builder, params); + return builder; + } + + @Override + public EnumSet context() { + return Metadata.API_AND_GATEWAY; + } + + /** + * @param decommissionAttribute decommission attribute + * @param status decommission status + * @param attributeType attribute type + * @param builder XContent builder + * @param params serialization parameters + */ + public static void toXContent( + DecommissionAttribute decommissionAttribute, + DecommissionStatus status, + String attributeType, + XContentBuilder builder, + ToXContent.Params params + ) throws IOException { + builder.startObject(attributeType); + builder.field(decommissionAttribute.attributeName(), decommissionAttribute.attributeValue()); + builder.endObject(); + builder.field("status", status); + } + + @Override + public String toString() { + return Strings.toString(this); + } +} diff --git a/server/src/test/java/org/opensearch/cluster/coordination/DecommissionNodeAttributeClusterStateTaskExecutorTests.java b/server/src/test/java/org/opensearch/cluster/coordination/DecommissionNodeAttributeClusterStateTaskExecutorTests.java deleted file mode 100644 index 204d31f18e2cf..0000000000000 --- a/server/src/test/java/org/opensearch/cluster/coordination/DecommissionNodeAttributeClusterStateTaskExecutorTests.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -package org.opensearch.cluster.coordination; - -import org.opensearch.Version; -import org.opensearch.cluster.ClusterName; -import org.opensearch.cluster.ClusterState; -import org.opensearch.cluster.ClusterStateTaskExecutor; -import org.opensearch.cluster.decommission.DecommissionAttribute; -import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.cluster.node.DiscoveryNodeRole; -import org.opensearch.cluster.node.DiscoveryNodes; -import org.opensearch.cluster.routing.allocation.AllocationService; -import org.opensearch.test.OpenSearchTestCase; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.atomic.AtomicReference; - -import static java.util.Collections.singletonMap; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -public class DecommissionNodeAttributeClusterStateTaskExecutorTests extends OpenSearchTestCase { - - public void testRemoveNodesForDecommissionedAttribute() throws Exception { - final AllocationService allocationService = mock(AllocationService.class); - when(allocationService.disassociateDeadNodes(any(ClusterState.class), eq(true), any(String.class))).thenAnswer( - im -> im.getArguments()[0] - ); - final AtomicReference remainingNodesClusterState = new AtomicReference<>(); - ClusterState clusterState = ClusterState.builder(new ClusterName("test")).build(); - - logger.info("--> adding five nodes on same zone_1"); - clusterState = addNodes(clusterState, "zone_1", "node1", "node2", "node3", "node4", "node5"); - - logger.info("--> adding five nodes on same zone_2"); - clusterState = addNodes(clusterState, "zone_2", "node6", "node7", "node8", "node9", "node10"); - - logger.info("--> adding five nodes on same zone_3"); - clusterState = addNodes(clusterState, "zone_3", "node11", "node12", "node13", "node14", "node15"); - - final DecommissionNodeAttributeClusterStateTaskExecutor executor = new DecommissionNodeAttributeClusterStateTaskExecutor( - allocationService, - logger - ) { - @Override - protected ClusterState remainingNodesClusterState(ClusterState currentState, DiscoveryNodes.Builder remainingNodesBuilder) { - remainingNodesClusterState.set(super.remainingNodesClusterState(currentState, remainingNodesBuilder)); - return remainingNodesClusterState.get(); - } - }; - - final List tasks = new ArrayList<>(); - tasks.add( - new DecommissionNodeAttributeClusterStateTaskExecutor.Task( - new DecommissionAttribute("zone", Collections.singletonList("zone_3")), - "unit test zone decommission executor" - ) - ); - - final ClusterStateTaskExecutor.ClusterTasksResult result = executor.execute( - clusterState, - tasks - ); - - ClusterState expectedClusterState = remainingNodesClusterState.get(); - ClusterState actualClusterState = result.resultingState; - - // Assert cluster state is updated and is successful - verify(allocationService).disassociateDeadNodes(eq(expectedClusterState), eq(true), any(String.class)); - assertEquals(actualClusterState, expectedClusterState); - assertTrue(result.executionResults.get(tasks.get(0)).isSuccess()); - - // Verify only 10 nodes present in the cluster after decommissioning - assertEquals(actualClusterState.nodes().getNodes().size(), 10); - - // Verify no nodes has attribute (zone, zone_3) - Iterator currDiscoveryNodeIterator = actualClusterState.nodes().getNodes().valuesIt(); - while (currDiscoveryNodeIterator.hasNext()) { - final DiscoveryNode node = currDiscoveryNodeIterator.next(); - assertNotEquals(node.getAttributes().get("zone"), "zone_3"); - } - } - - public void testSameClusterStateAfterExecutionForUnknownAttributeNameAndValue() throws Exception { - final AllocationService allocationService = mock(AllocationService.class); - when(allocationService.disassociateDeadNodes(any(ClusterState.class), eq(true), any(String.class))).thenAnswer( - im -> im.getArguments()[0] - ); - final AtomicReference remainingNodesClusterState = new AtomicReference<>(); - ClusterState clusterState = ClusterState.builder(new ClusterName("test")).build(); - - logger.info("--> adding five nodes on same zone_1"); - clusterState = addNodes(clusterState, "zone_1", "node1", "node2", "node3", "node4", "node5"); - - logger.info("--> adding five nodes on same zone_2"); - clusterState = addNodes(clusterState, "zone_2", "node6", "node7", "node8", "node9", "node10"); - - logger.info("--> adding five nodes on same zone_3"); - clusterState = addNodes(clusterState, "zone_3", "node11", "node12", "node13", "node14", "node15"); - - final DecommissionNodeAttributeClusterStateTaskExecutor executor = new DecommissionNodeAttributeClusterStateTaskExecutor( - allocationService, - logger - ) { - @Override - protected ClusterState remainingNodesClusterState(ClusterState currentState, DiscoveryNodes.Builder remainingNodesBuilder) { - remainingNodesClusterState.set(super.remainingNodesClusterState(currentState, remainingNodesBuilder)); - return remainingNodesClusterState.get(); - } - }; - - final List tasks = new ArrayList<>(); - // Task 1 with unknown attribute name - tasks.add( - new DecommissionNodeAttributeClusterStateTaskExecutor.Task( - new DecommissionAttribute("unknown_zone_name", Collections.singletonList("unknown_zone_value")), - "unit test zone decommission executor" - ) - ); - // Task 2 with unknown attribute value - tasks.add( - new DecommissionNodeAttributeClusterStateTaskExecutor.Task( - new DecommissionAttribute("zone", Collections.singletonList("unknown_zone_value")), - "unit test zone decommission executor" - ) - ); - - final ClusterStateTaskExecutor.ClusterTasksResult result = executor.execute( - clusterState, - tasks - ); - - ClusterState expectedClusterState = remainingNodesClusterState.get(); - ClusterState actualClusterState = result.resultingState; - - // assert that disassociate dead node tasks is never executed - verify(allocationService, never()).disassociateDeadNodes(eq(expectedClusterState), eq(true), any(String.class)); - - // assert that cluster state remains same - assertEquals(clusterState, actualClusterState); - - // Verify all 15 nodes present in the cluster after decommissioning unknown attribute name - assertEquals(actualClusterState.nodes().getNodes().size(), 15); - } - - private ClusterState addNodes(ClusterState clusterState, String zone, String... nodeIds) { - DiscoveryNodes.Builder nodeBuilder = DiscoveryNodes.builder(clusterState.nodes()); - org.opensearch.common.collect.List.of(nodeIds).forEach(nodeId -> nodeBuilder.add(newNode(nodeId, singletonMap("zone", zone)))); - clusterState = ClusterState.builder(clusterState).nodes(nodeBuilder).build(); - return clusterState; - } - - private DiscoveryNode newNode(String nodeId, Map attributes) { - return new DiscoveryNode(nodeId, buildNewFakeTransportAddress(), attributes, CLUSTER_MANAGER_DATA_ROLES, Version.CURRENT); - } - - final private static Set CLUSTER_MANAGER_DATA_ROLES = Collections.unmodifiableSet( - new HashSet<>(Arrays.asList(DiscoveryNodeRole.CLUSTER_MANAGER_ROLE, DiscoveryNodeRole.DATA_ROLE)) - ); -} From 0cc6d030e77b1764d7e872901b19c33e68a1dfa4 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Wed, 17 Aug 2022 17:38:25 +0530 Subject: [PATCH 022/187] Fixes Signed-off-by: Rishab Nahata --- .../cluster/decommission/DecommissionAttribute.java | 8 +++++++- .../cluster/decommission/DecommissionService.java | 6 ++++-- .../cluster/metadata/DecommissionAttributeMetadata.java | 5 ++--- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java index db4e06e854518..15c17ae4b7ae1 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java @@ -13,7 +13,6 @@ import org.opensearch.common.io.stream.Writeable; import java.io.IOException; -import java.util.List; import java.util.Objects; public final class DecommissionAttribute implements Writeable { @@ -102,4 +101,11 @@ public int hashCode() { } + @Override + public String toString() { + return "DecommissionAttribute{" + + "attributeName='" + attributeName + '\'' + + ", attributeValue='" + attributeValue + '\'' + + '}'; + } } diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 160b729b14b3a..d932a45401530 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -169,8 +169,9 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS // TODO - drain the nodes before decommissioning failDecommissionedNodes(newState); listener.onResponse(new ClusterStateUpdateResponse(true)); + } else { + listener.onResponse(new ClusterStateUpdateResponse(false)); } - listener.onResponse(new ClusterStateUpdateResponse(false)); } } ); @@ -196,7 +197,8 @@ private static void ensureNoAwarenessAttributeDecommissioned( private void failDecommissionedNodes(ClusterState state) { DecommissionAttributeMetadata decommissionAttributeMetadata = state.metadata().custom(DecommissionAttributeMetadata.TYPE); - assert decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSIONING) : "unexpected status encountered while decommissioning nodes"; + // TODO update the status check to DECOMMISSIONING once graceful decommission is implemented + assert decommissionAttributeMetadata.status().equals(DecommissionStatus.INIT) : "unexpected status encountered while decommissioning nodes"; DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute(); List nodesToBeDecommissioned = new ArrayList<>(); final Predicate shouldRemoveNodePredicate = discoveryNode -> nodeHasDecommissionedAttribute(discoveryNode, decommissionAttribute); diff --git a/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java b/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java index dd0c78d7519b1..869576f0ea070 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java @@ -150,8 +150,8 @@ public Version getMinimalSupportedVersion() { } public DecommissionAttributeMetadata(StreamInput in) throws IOException { - this.status = DecommissionStatus.fromValue(in.readByte()); this.decommissionAttribute = new DecommissionAttribute(in); + this.status = DecommissionStatus.fromValue(in.readByte()); } public static NamedDiff readDiffFrom(StreamInput in) throws IOException { @@ -165,7 +165,6 @@ public static NamedDiff readDiffFrom(StreamInput in) throws IOException public void writeTo(StreamOutput out) throws IOException { decommissionAttribute.writeTo(out); out.writeByte(status.value()); - out.writeString(attributeType); } public static DecommissionAttributeMetadata fromXContent(XContentParser parser) throws IOException { @@ -185,7 +184,7 @@ public static DecommissionAttributeMetadata fromXContent(XContentParser parser) String fieldName = parser.currentName(); String value; token = parser.nextToken(); - if (token != XContentParser.Token.VALUE_STRING) { + if (token == XContentParser.Token.VALUE_STRING) { value = parser.text(); } else { throw new OpenSearchParseException("failed to parse attribute [{}], expected string for attribute value", fieldName); From 16bf7940aa34cf4a45ef213db812d703f77ed02a Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Wed, 17 Aug 2022 19:11:09 +0530 Subject: [PATCH 023/187] Master abdication Signed-off-by: Rishab Nahata --- .../decommission/DecommissionService.java | 60 +++++++++++++++++-- 1 file changed, 56 insertions(+), 4 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index d932a45401530..40d848f81483c 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -12,6 +12,9 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; import org.opensearch.action.ActionListener; +import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsAction; +import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsRequest; +import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsResponse; import org.opensearch.cluster.ClusterChangedEvent; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateApplier; @@ -26,11 +29,15 @@ import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Priority; import org.opensearch.common.inject.Inject; +import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Settings; import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportException; +import org.opensearch.transport.TransportResponseHandler; import org.opensearch.transport.TransportService; +import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -61,7 +68,6 @@ public DecommissionService( this.clusterService = clusterService; this.transportService = transportService; this.threadPool = threadPool; - this.clusterState = clusterService.state(); // TODO - check if this is the right way this.nodeRemovalExecutor = new NodeRemovalClusterStateTaskExecutor(allocationService, logger); this.decommissionHelper = new DecommissionHelper( clusterService, @@ -84,16 +90,62 @@ private void setAwarenessAttributes(List awarenessAttributes) { public void initiateAttributeDecommissioning( final DecommissionAttribute decommissionAttribute, - final ActionListener listener + final ActionListener listener, + ClusterState state ) { - /** + /* * 1. Abdicate master * 2. Register attribute -> status should be set to INIT * 3. Trigger weigh away for graceful decommission -> status should be set to DECOMMISSIONING * 4. Once zone is weighed away -> trigger zone decommission using executor -> status should be set to DECOMMISSIONED on successful response * 5. Clear voting config */ - registerDecommissionAttribute(decommissionAttribute, listener); + this.clusterState = state; + abdicateDecommissionedClusterManagerNodes(decommissionAttribute, listener); + } + + private void abdicateDecommissionedClusterManagerNodes( + DecommissionAttribute decommissionAttribute, + final ActionListener listener + ) { + final Predicate shouldAbdicatePredicate = discoveryNode -> nodeHasDecommissionedAttribute(discoveryNode, decommissionAttribute); + List clusterManagerNodesToBeDecommissioned = new ArrayList<>(); + Iterator clusterManagerNodesIter = clusterState.nodes().getClusterManagerNodes().valuesIt(); + while (clusterManagerNodesIter.hasNext()) { + final DiscoveryNode node = clusterManagerNodesIter.next(); + if (shouldAbdicatePredicate.test(node)) { + clusterManagerNodesToBeDecommissioned.add(node.getName()); + } + } + transportService.sendRequest( + transportService.getLocalNode(), + AddVotingConfigExclusionsAction.NAME, + new AddVotingConfigExclusionsRequest(clusterManagerNodesToBeDecommissioned.toArray(String[]::new)), + new TransportResponseHandler() { + @Override + public void handleResponse(AddVotingConfigExclusionsResponse response) { + logger.info("successfully removed decommissioned cluster manager eligible nodes from voting config [{}], " + + "proceeding to drain the decommissioned nodes", response.toString()); + registerDecommissionAttribute(decommissionAttribute, listener); + } + + @Override + public void handleException(TransportException exp) { + logger.debug(new ParameterizedMessage( + "failure in removing decommissioned cluster manager eligible nodes from voting config"), exp); + } + + @Override + public String executor() { + return ThreadPool.Names.SAME; + } + + @Override + public AddVotingConfigExclusionsResponse read(StreamInput in) throws IOException { + return new AddVotingConfigExclusionsResponse(in); + } + } + ); } /** From 9c6fd13b96e76a1e9366018920c3bbb1baf8956a Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Wed, 17 Aug 2022 19:56:25 +0530 Subject: [PATCH 024/187] Fixes Signed-off-by: Rishab Nahata --- .../cluster/decommission/DecommissionService.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 40d848f81483c..48db0e06edb6a 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -101,6 +101,7 @@ public void initiateAttributeDecommissioning( * 5. Clear voting config */ this.clusterState = state; + logger.info("initiating awareness attribute [{}] decommissioning", decommissionAttribute.toString()); abdicateDecommissionedClusterManagerNodes(decommissionAttribute, listener); } @@ -124,8 +125,8 @@ private void abdicateDecommissionedClusterManagerNodes( new TransportResponseHandler() { @Override public void handleResponse(AddVotingConfigExclusionsResponse response) { - logger.info("successfully removed decommissioned cluster manager eligible nodes from voting config [{}], " + - "proceeding to drain the decommissioned nodes", response.toString()); + logger.info("successfully removed decommissioned cluster manager eligible nodes [{}] from voting config, " + + "proceeding to drain the decommissioned nodes", clusterManagerNodesToBeDecommissioned.toString()); registerDecommissionAttribute(decommissionAttribute, listener); } @@ -186,6 +187,7 @@ public void onFailure(String source, Exception e) { if (e instanceof DecommissionFailedException) { logger.error(() -> new ParameterizedMessage("failed to decommission attribute [{}]", decommissionAttribute.toString()), e); } else { + // could be due to on longer cluster manager clusterService.submitStateUpdateTask( "decommission_failed", new ClusterStateUpdateTask(Priority.URGENT) { From 1f38e8eaf988e0d1b66568738e0274b311a97572 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Wed, 17 Aug 2022 20:28:28 +0530 Subject: [PATCH 025/187] Update join validator to validate decommissioned node join request Signed-off-by: Rishab Nahata --- .../org/opensearch/OpenSearchException.java | 6 ++++ .../coordination/JoinTaskExecutor.java | 36 ++++++++++++++++--- .../NodeDecommissionedException.java | 32 +++++++++++++++++ 3 files changed, 70 insertions(+), 4 deletions(-) create mode 100644 server/src/main/java/org/opensearch/cluster/decommission/NodeDecommissionedException.java diff --git a/server/src/main/java/org/opensearch/OpenSearchException.java b/server/src/main/java/org/opensearch/OpenSearchException.java index d3e1bef9b6dbb..44d17cbca5652 100644 --- a/server/src/main/java/org/opensearch/OpenSearchException.java +++ b/server/src/main/java/org/opensearch/OpenSearchException.java @@ -1615,6 +1615,12 @@ private enum OpenSearchExceptionHandle { org.opensearch.cluster.decommission.DecommissionFailedException::new, 163, V_2_1_0 + ), + NODE_DECOMMISSIONED_EXCEPTION( + org.opensearch.cluster.decommission.NodeDecommissionedException.class, + org.opensearch.cluster.decommission.NodeDecommissionedException::new, + 163, + V_2_1_0 ); final Class exceptionClass; diff --git a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java index 5afdb5b12db23..dca706c250a20 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java @@ -39,6 +39,9 @@ import org.opensearch.cluster.ClusterStateTaskExecutor; import org.opensearch.cluster.NotClusterManagerException; import org.opensearch.cluster.block.ClusterBlocks; +import org.opensearch.cluster.decommission.DecommissionAttribute; +import org.opensearch.cluster.decommission.NodeDecommissionedException; +import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; @@ -107,7 +110,9 @@ public boolean isBecomeClusterManagerTask() { return reason.equals(BECOME_MASTER_TASK_REASON) || reason.equals(BECOME_CLUSTER_MANAGER_TASK_REASON); } - /** @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #isBecomeClusterManagerTask()} */ + /** + * @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #isBecomeClusterManagerTask()} + */ @Deprecated public boolean isBecomeMasterTask() { return isBecomeClusterManagerTask(); @@ -358,6 +363,7 @@ public boolean runOnlyOnClusterManager() { /** * a task indicates that the current node should become master + * * @deprecated As of 2.0, because supporting inclusive language, replaced by {@link #newBecomeClusterManagerTask()} */ @Deprecated @@ -384,8 +390,9 @@ public static Task newFinishElectionTask() { * Ensures that all indices are compatible with the given node version. This will ensure that all indices in the given metadata * will not be created with a newer version of opensearch as well as that all indices are newer or equal to the minimum index * compatibility version. - * @see Version#minimumIndexCompatibilityVersion() + * * @throws IllegalStateException if any index is incompatible with the given version + * @see Version#minimumIndexCompatibilityVersion() */ public static void ensureIndexCompatibility(final Version nodeVersion, Metadata metadata) { Version supportedIndexVersion = nodeVersion.minimumIndexCompatibilityVersion(); @@ -415,14 +422,18 @@ public static void ensureIndexCompatibility(final Version nodeVersion, Metadata } } - /** ensures that the joining node has a version that's compatible with all current nodes*/ + /** + * ensures that the joining node has a version that's compatible with all current nodes + */ public static void ensureNodesCompatibility(final Version joiningNodeVersion, DiscoveryNodes currentNodes) { final Version minNodeVersion = currentNodes.getMinNodeVersion(); final Version maxNodeVersion = currentNodes.getMaxNodeVersion(); ensureNodesCompatibility(joiningNodeVersion, minNodeVersion, maxNodeVersion); } - /** ensures that the joining node has a version that's compatible with a given version range */ + /** + * ensures that the joining node has a version that's compatible with a given version range + */ public static void ensureNodesCompatibility(Version joiningNodeVersion, Version minClusterNodeVersion, Version maxClusterNodeVersion) { assert minClusterNodeVersion.onOrBefore(maxClusterNodeVersion) : minClusterNodeVersion + " > " + maxClusterNodeVersion; if (joiningNodeVersion.isCompatible(maxClusterNodeVersion) == false) { @@ -466,6 +477,22 @@ public static void ensureMajorVersionBarrier(Version joiningNodeVersion, Version } } + public static void ensureNodeNotDecommissioned(DiscoveryNode node, Metadata metadata) { + DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.custom(DecommissionAttributeMetadata.TYPE); + if (decommissionAttributeMetadata != null) { + DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute(); + if (decommissionAttribute != null) { + if (node.getAttributes().get(decommissionAttribute.attributeName()).equals(decommissionAttribute.attributeValue())) { + throw new NodeDecommissionedException( + "node has decommissioned attribute [" + + decommissionAttribute.toString() + + "]." + ); + } + } + } + } + public static Collection> addBuiltInJoinValidators( Collection> onJoinValidators ) { @@ -473,6 +500,7 @@ public static Collection> addBuiltInJoin validators.add((node, state) -> { ensureNodesCompatibility(node.getVersion(), state.getNodes()); ensureIndexCompatibility(node.getVersion(), state.getMetadata()); + ensureNodeNotDecommissioned(node, state.getMetadata()); }); validators.addAll(onJoinValidators); return Collections.unmodifiableCollection(validators); diff --git a/server/src/main/java/org/opensearch/cluster/decommission/NodeDecommissionedException.java b/server/src/main/java/org/opensearch/cluster/decommission/NodeDecommissionedException.java new file mode 100644 index 0000000000000..d4ca4679a0872 --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/decommission/NodeDecommissionedException.java @@ -0,0 +1,32 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.decommission; + +import org.opensearch.OpenSearchException; +import org.opensearch.common.io.stream.StreamInput; + +import java.io.IOException; + +/** + * This exception is thrown if the node is decommissioned by @{@link DecommissionService} + * and this nodes needs to be removed from the cluster + * + * @opensearch.internal + */ + +public class NodeDecommissionedException extends OpenSearchException { + + public NodeDecommissionedException(String msg, Object... args) { + super(msg, args); + } + + public NodeDecommissionedException(StreamInput in) throws IOException { + super(in); + } +} From eaf1b55b5abbc1e1ef0e7d82893234d59c8f28ca Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Thu, 18 Aug 2022 10:57:37 +0530 Subject: [PATCH 026/187] Clear voting config after decommissioning Signed-off-by: Rishab Nahata --- .../decommission/DecommissionService.java | 48 +++++++++++++++++-- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 48db0e06edb6a..3971931180656 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -15,10 +15,14 @@ import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsAction; import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsRequest; import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsResponse; +import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsAction; +import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsRequest; +import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsResponse; import org.opensearch.cluster.ClusterChangedEvent; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateApplier; import org.opensearch.cluster.ClusterStateUpdateTask; +import org.opensearch.cluster.NotClusterManagerException; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; import org.opensearch.cluster.coordination.NodeRemovalClusterStateTaskExecutor; import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; @@ -34,6 +38,7 @@ import org.opensearch.common.settings.Settings; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportException; +import org.opensearch.transport.TransportResponse; import org.opensearch.transport.TransportResponseHandler; import org.opensearch.transport.TransportService; @@ -43,7 +48,6 @@ import java.util.List; import java.util.function.Predicate; -// do we need to implement ClusterStateApplier -> will a change in cluster state impact this service?? public class DecommissionService implements ClusterStateApplier { private static final Logger logger = LogManager.getLogger(DecommissionService.class); @@ -102,10 +106,10 @@ public void initiateAttributeDecommissioning( */ this.clusterState = state; logger.info("initiating awareness attribute [{}] decommissioning", decommissionAttribute.toString()); - abdicateDecommissionedClusterManagerNodes(decommissionAttribute, listener); + excludeDecommissionedClusterManagerNodesFromVotingConfig(decommissionAttribute, listener); } - private void abdicateDecommissionedClusterManagerNodes( + private void excludeDecommissionedClusterManagerNodesFromVotingConfig( DecommissionAttribute decommissionAttribute, final ActionListener listener ) { @@ -149,6 +153,38 @@ public AddVotingConfigExclusionsResponse read(StreamInput in) throws IOException ); } + private void clearVotingConfigAfterSuccessfulDecommission() { + final ClearVotingConfigExclusionsRequest clearVotingConfigExclusionsRequest = new ClearVotingConfigExclusionsRequest(); + clearVotingConfigExclusionsRequest.setWaitForRemoval(true); + transportService.sendRequest( + transportService.getLocalNode(), + ClearVotingConfigExclusionsAction.NAME, + clearVotingConfigExclusionsRequest, + new TransportResponseHandler() { + @Override + public void handleResponse(ClearVotingConfigExclusionsResponse response) { + logger.info("successfully cleared voting config after decommissioning"); + } + + @Override + public void handleException(TransportException exp) { + logger.debug(new ParameterizedMessage( + "failure in clearing voting config exclusion after decommissioning"), exp); + } + + @Override + public String executor() { + return ThreadPool.Names.SAME; + } + + @Override + public ClearVotingConfigExclusionsResponse read(StreamInput in) throws IOException { + return new ClearVotingConfigExclusionsResponse(in); + } + } + ); + } + /** * Registers new decommissioned attribute metadata in the cluster state *

@@ -186,6 +222,10 @@ public void onFailure(String source, Exception e) { // TODO - should we modify logic of logging for ease of debugging? if (e instanceof DecommissionFailedException) { logger.error(() -> new ParameterizedMessage("failed to decommission attribute [{}]", decommissionAttribute.toString()), e); + } else if (e instanceof NotClusterManagerException) { + logger.info(() -> new ParameterizedMessage( + "cluster-manager updated while executing request for decommission attribute [{}]", + decommissionAttribute.toString()), e); } else { // could be due to on longer cluster manager clusterService.submitStateUpdateTask( @@ -263,7 +303,9 @@ private void failDecommissionedNodes(ClusterState state) { nodesToBeDecommissioned.add(node); } } + // TODO - check for response from decommission request and then clear voting config? decommissionHelper.handleNodesDecommissionRequest(nodesToBeDecommissioned, "nodes-decommissioned"); + clearVotingConfigAfterSuccessfulDecommission(); } private static boolean nodeHasDecommissionedAttribute(DiscoveryNode discoveryNode, DecommissionAttribute decommissionAttribute) { From 184b345e5d1b3b04fc4d55e39e9521bf47b1f39e Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 23 Aug 2022 14:35:57 +0530 Subject: [PATCH 027/187] Resolving comments Signed-off-by: Rishab Nahata --- .../org/opensearch/OpenSearchException.java | 8 +- .../org/opensearch/cluster/ClusterModule.java | 7 +- .../coordination/JoinTaskExecutor.java | 8 +- .../decommission/DecommissionAttribute.java | 14 +- .../DecommissionFailedException.java | 6 + .../decommission/DecommissionHelper.java | 18 +-- .../decommission/DecommissionService.java | 123 +++++++++++------- .../decommission/DecommissionStatus.java | 78 +++++------ .../NodeDecommissionedException.java | 1 - .../DecommissionAttributeMetadata.java | 54 +++----- 10 files changed, 161 insertions(+), 156 deletions(-) diff --git a/server/src/main/java/org/opensearch/OpenSearchException.java b/server/src/main/java/org/opensearch/OpenSearchException.java index 44d17cbca5652..5ae83c9df70d3 100644 --- a/server/src/main/java/org/opensearch/OpenSearchException.java +++ b/server/src/main/java/org/opensearch/OpenSearchException.java @@ -34,7 +34,6 @@ import org.opensearch.action.support.replication.ReplicationOperation; import org.opensearch.cluster.action.shard.ShardStateAction; -import org.opensearch.cluster.decommission.DecommissionFailedException; import org.opensearch.common.CheckedFunction; import org.opensearch.common.Nullable; import org.opensearch.common.ParseField; @@ -69,6 +68,7 @@ import static java.util.Collections.singletonMap; import static java.util.Collections.unmodifiableMap; import static org.opensearch.Version.V_2_1_0; +import static org.opensearch.Version.V_2_3_0; import static org.opensearch.Version.V_3_0_0; import static org.opensearch.cluster.metadata.IndexMetadata.INDEX_UUID_NA_VALUE; import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken; @@ -1614,13 +1614,13 @@ private enum OpenSearchExceptionHandle { org.opensearch.cluster.decommission.DecommissionFailedException.class, org.opensearch.cluster.decommission.DecommissionFailedException::new, 163, - V_2_1_0 + V_2_3_0 ), NODE_DECOMMISSIONED_EXCEPTION( org.opensearch.cluster.decommission.NodeDecommissionedException.class, org.opensearch.cluster.decommission.NodeDecommissionedException::new, - 163, - V_2_1_0 + 164, + V_2_3_0 ); final Class exceptionClass; diff --git a/server/src/main/java/org/opensearch/cluster/ClusterModule.java b/server/src/main/java/org/opensearch/cluster/ClusterModule.java index de63369dafc89..115b9bdf3d8d6 100644 --- a/server/src/main/java/org/opensearch/cluster/ClusterModule.java +++ b/server/src/main/java/org/opensearch/cluster/ClusterModule.java @@ -192,7 +192,12 @@ public static List getNamedWriteables() { ComposableIndexTemplateMetadata::readDiffFrom ); registerMetadataCustom(entries, DataStreamMetadata.TYPE, DataStreamMetadata::new, DataStreamMetadata::readDiffFrom); - registerMetadataCustom(entries, DecommissionAttributeMetadata.TYPE, DecommissionAttributeMetadata::new, DecommissionAttributeMetadata::readDiffFrom); + registerMetadataCustom( + entries, + DecommissionAttributeMetadata.TYPE, + DecommissionAttributeMetadata::new, + DecommissionAttributeMetadata::readDiffFrom + ); // Task Status (not Diffable) entries.add(new Entry(Task.Status.class, PersistentTasksNodeService.Status.NAME, PersistentTasksNodeService.Status::new)); return entries; diff --git a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java index dca706c250a20..c309d2a3e06cc 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java @@ -110,9 +110,7 @@ public boolean isBecomeClusterManagerTask() { return reason.equals(BECOME_MASTER_TASK_REASON) || reason.equals(BECOME_CLUSTER_MANAGER_TASK_REASON); } - /** - * @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #isBecomeClusterManagerTask()} - */ + /** @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #isBecomeClusterManagerTask()} */ @Deprecated public boolean isBecomeMasterTask() { return isBecomeClusterManagerTask(); @@ -484,9 +482,7 @@ public static void ensureNodeNotDecommissioned(DiscoveryNode node, Metadata meta if (decommissionAttribute != null) { if (node.getAttributes().get(decommissionAttribute.attributeName()).equals(decommissionAttribute.attributeValue())) { throw new NodeDecommissionedException( - "node has decommissioned attribute [" - + decommissionAttribute.toString() - + "]." + "node [{}] has decommissioned attribute [{}].", node.getId(), decommissionAttribute.toString() ); } } diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java index 15c17ae4b7ae1..1eb6b488447a1 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java @@ -8,6 +8,7 @@ package org.opensearch.cluster.decommission; +import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.io.stream.Writeable; @@ -15,12 +16,17 @@ import java.io.IOException; import java.util.Objects; +/** + * {@link DecommissionAttribute} encapsulates information about decommissioned node attribute like attribute name, attribute value. + * + * @opensearch.internal + */ public final class DecommissionAttribute implements Writeable { private final String attributeName; private final String attributeValue; /** - * Update the attribute value for a given attribute name to decommission + * Construct new decommission attribute with updated value from a given decommission attribute * * @param decommissionAttribute current decommissioned attribute object * @param attributeValue attribute value to be updated with @@ -100,12 +106,8 @@ public int hashCode() { return Objects.hash(attributeName, attributeValue); } - @Override public String toString() { - return "DecommissionAttribute{" + - "attributeName='" + attributeName + '\'' + - ", attributeValue='" + attributeValue + '\'' + - '}'; + return "DecommissionAttribute{" + "attributeName='" + attributeName + '\'' + ", attributeValue='" + attributeValue + '\'' + '}'; } } diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionFailedException.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionFailedException.java index 3a611c2488779..3ba121dd90cee 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionFailedException.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionFailedException.java @@ -14,6 +14,12 @@ import java.io.IOException; +/** + * This exception is thrown whenever a failure occurs in decommission request @{@link DecommissionService} + * + * @opensearch.internal + */ + public class DecommissionFailedException extends OpenSearchException { private final DecommissionAttribute decommissionAttribute; diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java index d1eb17adc9747..ce7befacaef98 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java @@ -14,6 +14,7 @@ import org.opensearch.cluster.ClusterStateTaskListener; import org.opensearch.cluster.coordination.NodeRemovalClusterStateTaskExecutor; import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.routing.allocation.AllocationService; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Priority; @@ -21,6 +22,12 @@ import java.util.List; import java.util.Map; +/** + * Helper executor class to remove list of nodes from the cluster + * + * @opensearch.internal + */ + public class DecommissionHelper { private static final Logger logger = LogManager.getLogger(DecommissionHelper.class); @@ -28,20 +35,15 @@ public class DecommissionHelper { private final NodeRemovalClusterStateTaskExecutor nodeRemovalExecutor; private final ClusterService clusterService; - DecommissionHelper( - ClusterService clusterService, - NodeRemovalClusterStateTaskExecutor nodeRemovalClusterStateTaskExecutor - ) { - this.nodeRemovalExecutor = nodeRemovalClusterStateTaskExecutor; + DecommissionHelper(ClusterService clusterService, AllocationService allocationService) { this.clusterService = clusterService; + this.nodeRemovalExecutor = new NodeRemovalClusterStateTaskExecutor(allocationService, logger); } public void handleNodesDecommissionRequest(List nodesToBeDecommissioned, String reason) { final Map nodesDecommissionTasks = new LinkedHashMap<>(); nodesToBeDecommissioned.forEach(discoveryNode -> { - final NodeRemovalClusterStateTaskExecutor.Task task = new NodeRemovalClusterStateTaskExecutor.Task( - discoveryNode, reason - ); + final NodeRemovalClusterStateTaskExecutor.Task task = new NodeRemovalClusterStateTaskExecutor.Task(discoveryNode, reason); nodesDecommissionTasks.put(task, nodeRemovalExecutor); }); final String source = "node-decommissioned"; diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 3971931180656..ee5e657a500ce 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -38,7 +38,6 @@ import org.opensearch.common.settings.Settings; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportException; -import org.opensearch.transport.TransportResponse; import org.opensearch.transport.TransportResponseHandler; import org.opensearch.transport.TransportService; @@ -48,13 +47,17 @@ import java.util.List; import java.util.function.Predicate; +/** + * Service responsible for entire lifecycle of decommissioning and recommissioning an awareness attribute. + * + * @opensearch.internal + */ public class DecommissionService implements ClusterStateApplier { private static final Logger logger = LogManager.getLogger(DecommissionService.class); private final ClusterService clusterService; private final TransportService transportService; - private final NodeRemovalClusterStateTaskExecutor nodeRemovalExecutor; private final ThreadPool threadPool; private final DecommissionHelper decommissionHelper; private ClusterState clusterState; @@ -72,11 +75,7 @@ public DecommissionService( this.clusterService = clusterService; this.transportService = transportService; this.threadPool = threadPool; - this.nodeRemovalExecutor = new NodeRemovalClusterStateTaskExecutor(allocationService, logger); - this.decommissionHelper = new DecommissionHelper( - clusterService, - nodeRemovalExecutor - ); + this.decommissionHelper = new DecommissionHelper(clusterService, allocationService); this.awarenessAttributes = AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING.get(settings); clusterSettings.addSettingsUpdateConsumer( AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING, @@ -113,7 +112,10 @@ private void excludeDecommissionedClusterManagerNodesFromVotingConfig( DecommissionAttribute decommissionAttribute, final ActionListener listener ) { - final Predicate shouldAbdicatePredicate = discoveryNode -> nodeHasDecommissionedAttribute(discoveryNode, decommissionAttribute); + final Predicate shouldAbdicatePredicate = discoveryNode -> nodeHasDecommissionedAttribute( + discoveryNode, + decommissionAttribute + ); List clusterManagerNodesToBeDecommissioned = new ArrayList<>(); Iterator clusterManagerNodesIter = clusterState.nodes().getClusterManagerNodes().valuesIt(); while (clusterManagerNodesIter.hasNext()) { @@ -129,15 +131,20 @@ private void excludeDecommissionedClusterManagerNodesFromVotingConfig( new TransportResponseHandler() { @Override public void handleResponse(AddVotingConfigExclusionsResponse response) { - logger.info("successfully removed decommissioned cluster manager eligible nodes [{}] from voting config, " + - "proceeding to drain the decommissioned nodes", clusterManagerNodesToBeDecommissioned.toString()); + logger.info( + "successfully removed decommissioned cluster manager eligible nodes [{}] from voting config, " + + "proceeding to drain the decommissioned nodes", + clusterManagerNodesToBeDecommissioned.toString() + ); registerDecommissionAttribute(decommissionAttribute, listener); } @Override public void handleException(TransportException exp) { - logger.debug(new ParameterizedMessage( - "failure in removing decommissioned cluster manager eligible nodes from voting config"), exp); + logger.debug( + new ParameterizedMessage("failure in removing decommissioned cluster manager eligible nodes from voting config"), + exp + ); } @Override @@ -168,8 +175,7 @@ public void handleResponse(ClearVotingConfigExclusionsResponse response) { @Override public void handleException(TransportException exp) { - logger.debug(new ParameterizedMessage( - "failure in clearing voting config exclusion after decommissioning"), exp); + logger.debug(new ParameterizedMessage("failure in clearing voting config exclusion after decommissioning"), exp); } @Override @@ -200,6 +206,10 @@ private void registerDecommissionAttribute( final DecommissionAttribute decommissionAttribute, final ActionListener listener ) { + // check the local node is master and not in decommission attribute + assert transportService.getLocalNode().isClusterManagerNode() + && !nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute) + : "cannot register decommission attribute, as local node is not master or is going to be decommissioned"; validateAwarenessAttribute(decommissionAttribute, getAwarenessAttributes()); clusterService.submitStateUpdateTask( "put_decommission [" + decommissionAttribute + "]", @@ -218,47 +228,57 @@ public ClusterState execute(ClusterState currentState) throws Exception { @Override public void onFailure(String source, Exception e) { - // TODO - should we put the weights back to zone, since we weighed away the zone before we started registering the metadata + // TODO - should we put the weights back to zone, since we weighed away the zone before we started registering the + // metadata // TODO - should we modify logic of logging for ease of debugging? if (e instanceof DecommissionFailedException) { - logger.error(() -> new ParameterizedMessage("failed to decommission attribute [{}]", decommissionAttribute.toString()), e); + logger.error( + () -> new ParameterizedMessage("failed to decommission attribute [{}]", decommissionAttribute.toString()), + e + ); } else if (e instanceof NotClusterManagerException) { - logger.info(() -> new ParameterizedMessage( - "cluster-manager updated while executing request for decommission attribute [{}]", - decommissionAttribute.toString()), e); + logger.info( + () -> new ParameterizedMessage( + "cluster-manager updated while executing request for decommission attribute [{}]", + decommissionAttribute.toString() + ), + e + ); } else { // could be due to on longer cluster manager - clusterService.submitStateUpdateTask( - "decommission_failed", - new ClusterStateUpdateTask(Priority.URGENT) { - @Override - public ClusterState execute(ClusterState currentState) throws Exception { - Metadata metadata = currentState.metadata(); - Metadata.Builder mdBuilder = Metadata.builder(metadata); - logger.info("decommission request for attribute [{}] failed", decommissionAttribute.toString()); - DecommissionAttributeMetadata decommissionAttributeMetadata = new DecommissionAttributeMetadata( - decommissionAttribute, - DecommissionStatus.DECOMMISSION_FAILED - ); - mdBuilder.putCustom(DecommissionAttributeMetadata.TYPE, decommissionAttributeMetadata); - return ClusterState.builder(currentState).metadata(mdBuilder).build(); - } + clusterService.submitStateUpdateTask("decommission_failed", new ClusterStateUpdateTask(Priority.URGENT) { + @Override + public ClusterState execute(ClusterState currentState) throws Exception { + Metadata metadata = currentState.metadata(); + Metadata.Builder mdBuilder = Metadata.builder(metadata); + logger.info("decommission request for attribute [{}] failed", decommissionAttribute.toString()); + DecommissionAttributeMetadata decommissionAttributeMetadata = new DecommissionAttributeMetadata( + decommissionAttribute, + DecommissionStatus.DECOMMISSION_FAILED + ); + mdBuilder.putCustom(DecommissionAttributeMetadata.TYPE, decommissionAttributeMetadata); + return ClusterState.builder(currentState).metadata(mdBuilder).build(); + } - @Override - public void onFailure(String source, Exception e) { - logger.error(() -> new ParameterizedMessage( + @Override + public void onFailure(String source, Exception e) { + logger.error( + () -> new ParameterizedMessage( "failed to mark status as DECOMMISSION_FAILED for decommission attribute [{}]", - decommissionAttribute.toString()), e); -// listener.onFailure(e); - } + decommissionAttribute.toString() + ), + e + ); + // listener.onFailure(e); } - ); + }); } listener.onFailure(e); } @Override public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + assert !newState.equals(oldState) : "no update in cluster state after initiating decommission request."; if (!newState.equals(oldState)) { // TODO - drain the nodes before decommissioning failDecommissionedNodes(newState); @@ -283,19 +303,26 @@ private static void ensureNoAwarenessAttributeDecommissioned( DecommissionAttribute decommissionAttribute ) { // If the previous decommission request failed, we will allow the request to pass this check - if (decommissionAttributeMetadata != null && !decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_FAILED)) { - throw new DecommissionFailedException(decommissionAttribute, "one awareness attribute already decommissioned, " + - "recommission before triggering another decommission"); + if (decommissionAttributeMetadata != null + && !decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_FAILED)) { + throw new DecommissionFailedException( + decommissionAttribute, + "one awareness attribute already decommissioned, " + "recommission before triggering another decommission" + ); } } private void failDecommissionedNodes(ClusterState state) { DecommissionAttributeMetadata decommissionAttributeMetadata = state.metadata().custom(DecommissionAttributeMetadata.TYPE); // TODO update the status check to DECOMMISSIONING once graceful decommission is implemented - assert decommissionAttributeMetadata.status().equals(DecommissionStatus.INIT) : "unexpected status encountered while decommissioning nodes"; + assert decommissionAttributeMetadata.status().equals(DecommissionStatus.INIT) + : "unexpected status encountered while decommissioning nodes"; DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute(); List nodesToBeDecommissioned = new ArrayList<>(); - final Predicate shouldRemoveNodePredicate = discoveryNode -> nodeHasDecommissionedAttribute(discoveryNode, decommissionAttribute); + final Predicate shouldRemoveNodePredicate = discoveryNode -> nodeHasDecommissionedAttribute( + discoveryNode, + decommissionAttribute + ); Iterator nodesIter = state.nodes().getNodes().valuesIt(); while (nodesIter.hasNext()) { final DiscoveryNode node = nodesIter.next(); @@ -309,9 +336,7 @@ private void failDecommissionedNodes(ClusterState state) { } private static boolean nodeHasDecommissionedAttribute(DiscoveryNode discoveryNode, DecommissionAttribute decommissionAttribute) { - return discoveryNode.getAttributes().get( - decommissionAttribute.attributeName() - ).equals(decommissionAttribute.attributeValue()); + return discoveryNode.getAttributes().get(decommissionAttribute.attributeName()).equals(decommissionAttribute.attributeValue()); } @Override diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java index 8f4ca3a6f578a..d091b8ab44e30 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java @@ -8,87 +8,73 @@ package org.opensearch.cluster.decommission; +/** + * An enumeration of the states during decommissioning and recommissioning. + */ public enum DecommissionStatus { /** * Decommission process is initiated */ - INIT((byte) 0), + INIT("init"), /** * Decommission process has started, decommissioned nodes should be weighed away */ - DECOMMISSIONING((byte) 1), + DECOMMISSION_IN_PROGRESS("decommission_in_progress"), /** * Decommissioning awareness attribute completed */ - DECOMMISSIONED((byte) 2), + DECOMMISSION_SUCCESSFUL("decommission_successful"), /** * Decommission request failed */ - DECOMMISSION_FAILED((byte) 3), + DECOMMISSION_FAILED("decommission_failed"), /** * Recommission request received, recommissioning process has started */ - RECOMMISSIONING((byte) 4), + RECOMMISSION_IN_PROGRESS("recommission_in_progress"), /** * Recommission request failed. No nodes should fail to join the cluster with decommission exception */ - RECOMMISSION_FAILED((byte) 5); + RECOMMISSION_FAILED("recommission_failed"); - private final byte value; + private final String status; - DecommissionStatus(byte value) { - this.value = value; + DecommissionStatus(String status) { + this.status = status; } /** - * Returns code that represents the decommission state + * Returns status that represents the decommission state * - * @return code for the state + * @return status */ - public byte value() { - return value; + public String status() { + return status; } /** - * Generate decommission state from code + * Generate decommission status from given string * - * @param value the state code - * @return state + * @param status status in string + * @return status */ - public static DecommissionStatus fromValue(byte value) { - switch (value) { - case 0: - return INIT; - case 1: - return DECOMMISSIONING; - case 2: - return DECOMMISSIONED; - case 3: - return DECOMMISSION_FAILED; - case 4: - return RECOMMISSIONING; - case 5: - return RECOMMISSION_FAILED; - default: - throw new IllegalArgumentException("No decommission state for value [" + value + "]"); - } - } - public static DecommissionStatus fromString(String status) { - if ("init".equals(status)) { + if (status == null) { + throw new IllegalArgumentException("decommission status cannot be null"); + } + if (status.equals(INIT.status())) { return INIT; - } else if ("decommissioning".equals(status)) { - return DECOMMISSIONING; - } else if ("decommissioned".equals(status)) { - return DECOMMISSIONED; - } else if ("decommission_failed".equals(status)) { + } else if (status.equals(DECOMMISSION_IN_PROGRESS.status())) { + return DECOMMISSION_IN_PROGRESS; + } else if (status.equals(DECOMMISSION_SUCCESSFUL.status())) { + return DECOMMISSION_SUCCESSFUL; + } else if (status.equals(DECOMMISSION_FAILED.status())) { return DECOMMISSION_FAILED; - } else if ("recommissioning".equals(status)) { - return RECOMMISSIONING; - } else if ("recommission_failed".equals(status)) { + } else if (status.equals(RECOMMISSION_IN_PROGRESS.status())) { + return RECOMMISSION_IN_PROGRESS; + } else if (status.equals(RECOMMISSION_FAILED.status())) { return RECOMMISSION_FAILED; } - throw new IllegalStateException("No status match for [" + status + "]"); + throw new IllegalStateException("Decommission status [" + status + "] not recognized."); } } - diff --git a/server/src/main/java/org/opensearch/cluster/decommission/NodeDecommissionedException.java b/server/src/main/java/org/opensearch/cluster/decommission/NodeDecommissionedException.java index d4ca4679a0872..847d5a527b017 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/NodeDecommissionedException.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/NodeDecommissionedException.java @@ -19,7 +19,6 @@ * * @opensearch.internal */ - public class NodeDecommissionedException extends OpenSearchException { public NodeDecommissionedException(String msg, Object... args) { diff --git a/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java b/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java index 869576f0ea070..258f2a4e1b2a8 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java @@ -57,8 +57,7 @@ public DecommissionAttributeMetadata(DecommissionAttribute decommissionAttribute * @param decommissionAttribute attribute details */ public DecommissionAttributeMetadata(DecommissionAttribute decommissionAttribute) { - this.decommissionAttribute = decommissionAttribute; - this.status = DecommissionStatus.INIT; + this(decommissionAttribute, DecommissionStatus.INIT); } /** @@ -79,13 +78,8 @@ public DecommissionStatus status() { return this.status; } - public DecommissionAttributeMetadata withUpdatedStatus( - DecommissionAttributeMetadata metadata, - DecommissionStatus status) { - return new DecommissionAttributeMetadata( - metadata.decommissionAttribute(), - status - ); + public DecommissionAttributeMetadata withUpdatedStatus(DecommissionAttributeMetadata metadata, DecommissionStatus status) { + return new DecommissionAttributeMetadata(metadata.decommissionAttribute(), status); } /** @@ -95,10 +89,7 @@ public DecommissionAttributeMetadata withUpdatedStatus( * @param attributeValue new attribute value * @return new instance with updated attribute value and status as DecommissionStatus.INIT */ - public DecommissionAttributeMetadata withUpdatedAttributeValue( - DecommissionAttributeMetadata metadata, - String attributeValue - ) { + public DecommissionAttributeMetadata withUpdatedAttributeValue(DecommissionAttributeMetadata metadata, String attributeValue) { return new DecommissionAttributeMetadata( new DecommissionAttribute(metadata.decommissionAttribute, attributeValue), DecommissionStatus.INIT @@ -116,21 +107,6 @@ public boolean equals(Object o) { return decommissionAttribute.equals(that.decommissionAttribute); } - /** - * Checks if this instance and the given instance share the same decommissioned attributeName - * and only differ in the attributeValue {@link DecommissionAttribute#attributeValue()} - * - * @param other other decommission attribute metadata - * @return {@code true} iff both instances contain the same attributeName - */ - public boolean equalsIgnoreValue(@Nullable DecommissionAttributeMetadata other) { - if (other == null) { - return false; - } - if (!status.equals(other.status)) return false; - return decommissionAttribute.equalsIgnoreValues(other.decommissionAttribute); - } - @Override public int hashCode() { return Objects.hash(attributeType, decommissionAttribute, status); @@ -146,12 +122,12 @@ public String getWriteableName() { @Override public Version getMinimalSupportedVersion() { - return Version.CURRENT.minimumCompatibilityVersion(); + return Version.V_2_3_0; } public DecommissionAttributeMetadata(StreamInput in) throws IOException { this.decommissionAttribute = new DecommissionAttribute(in); - this.status = DecommissionStatus.fromValue(in.readByte()); + this.status = DecommissionStatus.fromString(in.readString()); } public static NamedDiff readDiffFrom(StreamInput in) throws IOException { @@ -164,7 +140,7 @@ public static NamedDiff readDiffFrom(StreamInput in) throws IOException @Override public void writeTo(StreamOutput out) throws IOException { decommissionAttribute.writeTo(out); - out.writeByte(status.value()); + out.writeString(status.status()); } public static DecommissionAttributeMetadata fromXContent(XContentParser parser) throws IOException { @@ -176,7 +152,10 @@ public static DecommissionAttributeMetadata fromXContent(XContentParser parser) String currentFieldName = parser.currentName(); if (attributeType.equals(currentFieldName)) { if (parser.nextToken() != XContentParser.Token.START_OBJECT) { - throw new OpenSearchParseException("failed to parse decommission attribute type [{}], expected object", attributeType); + throw new OpenSearchParseException( + "failed to parse decommission attribute type [{}], expected object", + attributeType + ); } token = parser.nextToken(); if (token != XContentParser.Token.END_OBJECT) { @@ -187,7 +166,10 @@ public static DecommissionAttributeMetadata fromXContent(XContentParser parser) if (token == XContentParser.Token.VALUE_STRING) { value = parser.text(); } else { - throw new OpenSearchParseException("failed to parse attribute [{}], expected string for attribute value", fieldName); + throw new OpenSearchParseException( + "failed to parse attribute [{}], expected string for attribute value", + fieldName + ); } decommissionAttribute = new DecommissionAttribute(fieldName, value); } else { @@ -198,7 +180,9 @@ public static DecommissionAttributeMetadata fromXContent(XContentParser parser) } } else if ("status".equals(currentFieldName)) { if (parser.nextToken() != XContentParser.Token.VALUE_STRING) { - throw new OpenSearchParseException("failed to parse status of decommissioning, expected string but found unknown type"); + throw new OpenSearchParseException( + "failed to parse status of decommissioning, expected string but found unknown type" + ); } status = DecommissionStatus.fromString(parser.text()); } else { @@ -243,7 +227,7 @@ public static void toXContent( builder.startObject(attributeType); builder.field(decommissionAttribute.attributeName(), decommissionAttribute.attributeValue()); builder.endObject(); - builder.field("status", status); + builder.field("status", status.status()); } @Override From fca338a751528bf30f06102721b120011098ac8d Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 23 Aug 2022 15:24:43 +0530 Subject: [PATCH 028/187] Fixes Signed-off-by: Rishab Nahata --- .../decommission/DecommissionAttribute.java | 20 ------------------- .../decommission/DecommissionService.java | 7 ++++--- .../DecommissionAttributeMetadata.java | 14 ------------- 3 files changed, 4 insertions(+), 37 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java index 1eb6b488447a1..1a30b59d5a60e 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java @@ -25,16 +25,6 @@ public final class DecommissionAttribute implements Writeable { private final String attributeName; private final String attributeValue; - /** - * Construct new decommission attribute with updated value from a given decommission attribute - * - * @param decommissionAttribute current decommissioned attribute object - * @param attributeValue attribute value to be updated with - */ - public DecommissionAttribute(DecommissionAttribute decommissionAttribute, String attributeValue) { - this(decommissionAttribute.attributeName, attributeValue); - } - /** * Constructs new decommission attribute name value pair * @@ -80,16 +70,6 @@ public void writeTo(StreamOutput out) throws IOException { out.writeString(attributeValue); } - /** - * Checks if this instance is equal to the other instance in attributeName but differ in attribute value {@link #attributeValue}. - * - * @param other other decommission attribute name value - * @return {@code true} if both instances equal in attributeName fields but the attributeValue field - */ - public boolean equalsIgnoreValues(DecommissionAttribute other) { - return attributeName.equals(other.attributeName); - } - @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index ee5e657a500ce..53813ead9fcad 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -103,6 +103,7 @@ public void initiateAttributeDecommissioning( * 4. Once zone is weighed away -> trigger zone decommission using executor -> status should be set to DECOMMISSIONED on successful response * 5. Clear voting config */ + validateAwarenessAttribute(decommissionAttribute, getAwarenessAttributes()); this.clusterState = state; logger.info("initiating awareness attribute [{}] decommissioning", decommissionAttribute.toString()); excludeDecommissionedClusterManagerNodesFromVotingConfig(decommissionAttribute, listener); @@ -112,7 +113,7 @@ private void excludeDecommissionedClusterManagerNodesFromVotingConfig( DecommissionAttribute decommissionAttribute, final ActionListener listener ) { - final Predicate shouldAbdicatePredicate = discoveryNode -> nodeHasDecommissionedAttribute( + final Predicate shouldDecommissionPredicate = discoveryNode -> nodeHasDecommissionedAttribute( discoveryNode, decommissionAttribute ); @@ -120,7 +121,7 @@ private void excludeDecommissionedClusterManagerNodesFromVotingConfig( Iterator clusterManagerNodesIter = clusterState.nodes().getClusterManagerNodes().valuesIt(); while (clusterManagerNodesIter.hasNext()) { final DiscoveryNode node = clusterManagerNodesIter.next(); - if (shouldAbdicatePredicate.test(node)) { + if (shouldDecommissionPredicate.test(node)) { clusterManagerNodesToBeDecommissioned.add(node.getName()); } } @@ -210,7 +211,7 @@ private void registerDecommissionAttribute( assert transportService.getLocalNode().isClusterManagerNode() && !nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute) : "cannot register decommission attribute, as local node is not master or is going to be decommissioned"; - validateAwarenessAttribute(decommissionAttribute, getAwarenessAttributes()); + clusterService.submitStateUpdateTask( "put_decommission [" + decommissionAttribute + "]", new ClusterStateUpdateTask(Priority.URGENT) { diff --git a/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java b/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java index 258f2a4e1b2a8..6cba47d229534 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java @@ -82,20 +82,6 @@ public DecommissionAttributeMetadata withUpdatedStatus(DecommissionAttributeMeta return new DecommissionAttributeMetadata(metadata.decommissionAttribute(), status); } - /** - * Creates a new instance with a updated attribute value. - * - * @param metadata current metadata - * @param attributeValue new attribute value - * @return new instance with updated attribute value and status as DecommissionStatus.INIT - */ - public DecommissionAttributeMetadata withUpdatedAttributeValue(DecommissionAttributeMetadata metadata, String attributeValue) { - return new DecommissionAttributeMetadata( - new DecommissionAttribute(metadata.decommissionAttribute, attributeValue), - DecommissionStatus.INIT - ); - } - @Override public boolean equals(Object o) { if (this == o) return true; From 3d0ce59f212b53af8df5f1fd2e57d0cfa39fc3a4 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 23 Aug 2022 16:46:33 +0530 Subject: [PATCH 029/187] Fixes Signed-off-by: Rishab Nahata --- .../decommission/DecommissionService.java | 55 ++++++++++++++++--- .../decommission/DecommissionStatus.java | 6 +- .../DecommissionAttributeMetadata.java | 14 +++-- 3 files changed, 58 insertions(+), 17 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 53813ead9fcad..fe67723187eab 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -24,7 +24,6 @@ import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.NotClusterManagerException; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; -import org.opensearch.cluster.coordination.NodeRemovalClusterStateTaskExecutor; import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; @@ -50,6 +49,17 @@ /** * Service responsible for entire lifecycle of decommissioning and recommissioning an awareness attribute. * + * Whenever a cluster manager initiates operation to decommission an awareness attribute, + * the service makes the best attempt to perform the following task - + *

+ * 1. Remove cluster-manager eligible nodes from voting config [TODO - checks to avoid quorum loss scenarios] + * 2. Initiates nodes decommissioning by adding custom metadata with the attribute and state as {@link DecommissionStatus#DECOMMISSION_INIT} + * 3. Triggers weigh away for nodes having given awareness attribute to drain. This marks the decommission status as {@link DecommissionStatus#DECOMMISSION_IN_PROGRESS} + * 4. Once weighed away, the service triggers nodes decommission + * 5. Once the decommission is successful, the service clears the voting config and marks the status as {@link DecommissionStatus#DECOMMISSION_SUCCESSFUL} + * 6. If service fails at any step, it would mark the status as {@link DecommissionStatus#DECOMMISSION_FAILED} + *

+ * * @opensearch.internal */ public class DecommissionService implements ClusterStateApplier { @@ -96,13 +106,6 @@ public void initiateAttributeDecommissioning( final ActionListener listener, ClusterState state ) { - /* - * 1. Abdicate master - * 2. Register attribute -> status should be set to INIT - * 3. Trigger weigh away for graceful decommission -> status should be set to DECOMMISSIONING - * 4. Once zone is weighed away -> trigger zone decommission using executor -> status should be set to DECOMMISSIONED on successful response - * 5. Clear voting config - */ validateAwarenessAttribute(decommissionAttribute, getAwarenessAttributes()); this.clusterState = state; logger.info("initiating awareness attribute [{}] decommissioning", decommissionAttribute.toString()); @@ -292,6 +295,40 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS ); } + // To Do - Can we add a consumer here such that whenever this succeeds we call the next method in on cluster state processed + private void updateMetadataWithDecommissionStatus( + DecommissionStatus decommissionStatus + ) { + clusterService.submitStateUpdateTask( + decommissionStatus.status(), + new ClusterStateUpdateTask(Priority.URGENT) { + @Override + public ClusterState execute(ClusterState currentState) throws Exception { + Metadata metadata = currentState.metadata(); + DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.custom(DecommissionAttributeMetadata.TYPE); + assert decommissionAttributeMetadata != null + && decommissionAttributeMetadata.decommissionAttribute() != null + : "failed to update status for decommission. metadata doesn't exist or invalid"; + Metadata.Builder mdBuilder = Metadata.builder(metadata); + DecommissionAttributeMetadata newMetadata = decommissionAttributeMetadata.withUpdatedStatus(decommissionStatus); + mdBuilder.putCustom(DecommissionAttributeMetadata.TYPE, newMetadata); + return ClusterState.builder(currentState).metadata(mdBuilder).build(); + } + + @Override + public void onFailure(String source, Exception e) { + logger.error( + () -> new ParameterizedMessage( + "failed to mark status as [{}]", + decommissionStatus.status() + ), + e + ); + } + } + ); + } + private static void validateAwarenessAttribute(final DecommissionAttribute decommissionAttribute, List awarenessAttributes) { if (!awarenessAttributes.contains(decommissionAttribute.attributeName())) { throw new DecommissionFailedException(decommissionAttribute, "invalid awareness attribute requested for decommissioning"); @@ -316,7 +353,7 @@ private static void ensureNoAwarenessAttributeDecommissioned( private void failDecommissionedNodes(ClusterState state) { DecommissionAttributeMetadata decommissionAttributeMetadata = state.metadata().custom(DecommissionAttributeMetadata.TYPE); // TODO update the status check to DECOMMISSIONING once graceful decommission is implemented - assert decommissionAttributeMetadata.status().equals(DecommissionStatus.INIT) + assert decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_INIT) : "unexpected status encountered while decommissioning nodes"; DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute(); List nodesToBeDecommissioned = new ArrayList<>(); diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java index d091b8ab44e30..41f9acfbc35d7 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java @@ -15,7 +15,7 @@ public enum DecommissionStatus { /** * Decommission process is initiated */ - INIT("init"), + DECOMMISSION_INIT("decommission_init"), /** * Decommission process has started, decommissioned nodes should be weighed away */ @@ -62,8 +62,8 @@ public static DecommissionStatus fromString(String status) { if (status == null) { throw new IllegalArgumentException("decommission status cannot be null"); } - if (status.equals(INIT.status())) { - return INIT; + if (status.equals(DECOMMISSION_INIT.status())) { + return DECOMMISSION_INIT; } else if (status.equals(DECOMMISSION_IN_PROGRESS.status())) { return DECOMMISSION_IN_PROGRESS; } else if (status.equals(DECOMMISSION_SUCCESSFUL.status())) { diff --git a/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java b/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java index 6cba47d229534..c6252e9981810 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java @@ -15,7 +15,6 @@ import org.opensearch.cluster.decommission.DecommissionAttribute; import org.opensearch.cluster.decommission.DecommissionStatus; import org.opensearch.cluster.metadata.Metadata.Custom; -import org.opensearch.common.Nullable; import org.opensearch.common.Strings; import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.io.stream.StreamOutput; @@ -52,12 +51,12 @@ public DecommissionAttributeMetadata(DecommissionAttribute decommissionAttribute } /** - * Constructs new decommission attribute metadata with status as {@link DecommissionStatus#INIT} + * Constructs new decommission attribute metadata with status as {@link DecommissionStatus#DECOMMISSION_INIT} * * @param decommissionAttribute attribute details */ public DecommissionAttributeMetadata(DecommissionAttribute decommissionAttribute) { - this(decommissionAttribute, DecommissionStatus.INIT); + this(decommissionAttribute, DecommissionStatus.DECOMMISSION_INIT); } /** @@ -78,8 +77,13 @@ public DecommissionStatus status() { return this.status; } - public DecommissionAttributeMetadata withUpdatedStatus(DecommissionAttributeMetadata metadata, DecommissionStatus status) { - return new DecommissionAttributeMetadata(metadata.decommissionAttribute(), status); + /** + * Creates a new instance that has the given decommission attribute moved to the given @{@link DecommissionStatus} + * @param status status to be updated with + * @return new instance with updated status + */ + public DecommissionAttributeMetadata withUpdatedStatus(DecommissionStatus status) { + return new DecommissionAttributeMetadata(decommissionAttribute(), status); } @Override From 166f6885053d439b2d013b0068b6c89ed4b9ffd3 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 23 Aug 2022 18:07:09 +0530 Subject: [PATCH 030/187] Some refactpring Signed-off-by: Rishab Nahata --- .../decommission/DecommissionService.java | 76 ++++++++----------- 1 file changed, 31 insertions(+), 45 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index fe67723187eab..912fcda7baa62 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -21,6 +21,7 @@ import org.opensearch.cluster.ClusterChangedEvent; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateApplier; +import org.opensearch.cluster.ClusterStateTaskListener; import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.NotClusterManagerException; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; @@ -210,7 +211,6 @@ private void registerDecommissionAttribute( final DecommissionAttribute decommissionAttribute, final ActionListener listener ) { - // check the local node is master and not in decommission attribute assert transportService.getLocalNode().isClusterManagerNode() && !nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute) : "cannot register decommission attribute, as local node is not master or is going to be decommissioned"; @@ -219,8 +219,12 @@ private void registerDecommissionAttribute( "put_decommission [" + decommissionAttribute + "]", new ClusterStateUpdateTask(Priority.URGENT) { @Override - public ClusterState execute(ClusterState currentState) throws Exception { - logger.info("decommission request for attribute [{}] received", decommissionAttribute.toString()); + public ClusterState execute(ClusterState currentState) { + logger.info( + "registering decommission metadata for attribute [{}] with status as [{}]", + decommissionAttribute.toString(), + DecommissionStatus.DECOMMISSION_INIT + ); Metadata metadata = currentState.metadata(); Metadata.Builder mdBuilder = Metadata.builder(metadata); DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.custom(DecommissionAttributeMetadata.TYPE); @@ -232,69 +236,51 @@ public ClusterState execute(ClusterState currentState) throws Exception { @Override public void onFailure(String source, Exception e) { - // TODO - should we put the weights back to zone, since we weighed away the zone before we started registering the - // metadata - // TODO - should we modify logic of logging for ease of debugging? if (e instanceof DecommissionFailedException) { logger.error( - () -> new ParameterizedMessage("failed to decommission attribute [{}]", decommissionAttribute.toString()), + () -> new ParameterizedMessage( + "failed to decommission attribute [{}]", + decommissionAttribute.toString()), e ); + listener.onFailure(e); } else if (e instanceof NotClusterManagerException) { - logger.info( + logger.debug( () -> new ParameterizedMessage( "cluster-manager updated while executing request for decommission attribute [{}]", decommissionAttribute.toString() ), e ); + // Do we need a listener here as the transport request will be retried? } else { - // could be due to on longer cluster manager - clusterService.submitStateUpdateTask("decommission_failed", new ClusterStateUpdateTask(Priority.URGENT) { - @Override - public ClusterState execute(ClusterState currentState) throws Exception { - Metadata metadata = currentState.metadata(); - Metadata.Builder mdBuilder = Metadata.builder(metadata); - logger.info("decommission request for attribute [{}] failed", decommissionAttribute.toString()); - DecommissionAttributeMetadata decommissionAttributeMetadata = new DecommissionAttributeMetadata( - decommissionAttribute, - DecommissionStatus.DECOMMISSION_FAILED - ); - mdBuilder.putCustom(DecommissionAttributeMetadata.TYPE, decommissionAttributeMetadata); - return ClusterState.builder(currentState).metadata(mdBuilder).build(); - } - - @Override - public void onFailure(String source, Exception e) { - logger.error( - () -> new ParameterizedMessage( - "failed to mark status as DECOMMISSION_FAILED for decommission attribute [{}]", - decommissionAttribute.toString() - ), - e - ); - // listener.onFailure(e); - } - }); + logger.error( + () -> new ParameterizedMessage( + "failed to initiate decommissioning for attribute [{}]", + decommissionAttribute.toString() + ), + e + ); + updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_FAILED); + listener.onFailure(e); } - listener.onFailure(e); } @Override public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { assert !newState.equals(oldState) : "no update in cluster state after initiating decommission request."; - if (!newState.equals(oldState)) { - // TODO - drain the nodes before decommissioning - failDecommissionedNodes(newState); - listener.onResponse(new ClusterStateUpdateResponse(true)); - } else { - listener.onResponse(new ClusterStateUpdateResponse(false)); - } + // Do we attach a listener here with failed acknowledgement to the request? + listener.onResponse(new ClusterStateUpdateResponse(true)); + initiateGracefulDecommission(newState, listener); } } ); } + private void initiateGracefulDecommission(ClusterState clusterState, ActionListener listener) { + failDecommissionedNodes(clusterState, listener); + } + // To Do - Can we add a consumer here such that whenever this succeeds we call the next method in on cluster state processed private void updateMetadataWithDecommissionStatus( DecommissionStatus decommissionStatus @@ -345,12 +331,12 @@ private static void ensureNoAwarenessAttributeDecommissioned( && !decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_FAILED)) { throw new DecommissionFailedException( decommissionAttribute, - "one awareness attribute already decommissioned, " + "recommission before triggering another decommission" + "one awareness attribute already decommissioned, recommission before triggering another decommission" ); } } - private void failDecommissionedNodes(ClusterState state) { + private void failDecommissionedNodes(ClusterState state, ActionListener listener) { DecommissionAttributeMetadata decommissionAttributeMetadata = state.metadata().custom(DecommissionAttributeMetadata.TYPE); // TODO update the status check to DECOMMISSIONING once graceful decommission is implemented assert decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_INIT) From a0cb7fdc49f0e26f83b7c55c60df4492aafce14d Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 23 Aug 2022 19:35:36 +0530 Subject: [PATCH 031/187] Updates Signed-off-by: Rishab Nahata --- .../decommission/DecommissionService.java | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 912fcda7baa62..53e73f98e8590 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -21,7 +21,6 @@ import org.opensearch.cluster.ClusterChangedEvent; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateApplier; -import org.opensearch.cluster.ClusterStateTaskListener; import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.NotClusterManagerException; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; @@ -49,7 +48,7 @@ /** * Service responsible for entire lifecycle of decommissioning and recommissioning an awareness attribute. - * + *

* Whenever a cluster manager initiates operation to decommission an awareness attribute, * the service makes the best attempt to perform the following task - *

@@ -271,14 +270,14 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS assert !newState.equals(oldState) : "no update in cluster state after initiating decommission request."; // Do we attach a listener here with failed acknowledgement to the request? listener.onResponse(new ClusterStateUpdateResponse(true)); - initiateGracefulDecommission(newState, listener); + initiateGracefulDecommission(newState); } } ); } - private void initiateGracefulDecommission(ClusterState clusterState, ActionListener listener) { - failDecommissionedNodes(clusterState, listener); + private void initiateGracefulDecommission(ClusterState clusterState) { + failDecommissionedNodes(clusterState); } // To Do - Can we add a consumer here such that whenever this succeeds we call the next method in on cluster state processed @@ -295,6 +294,7 @@ public ClusterState execute(ClusterState currentState) throws Exception { assert decommissionAttributeMetadata != null && decommissionAttributeMetadata.decommissionAttribute() != null : "failed to update status for decommission. metadata doesn't exist or invalid"; + assert assertIncrementalStatusOrFailed(decommissionAttributeMetadata.status(), decommissionStatus); Metadata.Builder mdBuilder = Metadata.builder(metadata); DecommissionAttributeMetadata newMetadata = decommissionAttributeMetadata.withUpdatedStatus(decommissionStatus); mdBuilder.putCustom(DecommissionAttributeMetadata.TYPE, newMetadata); @@ -336,7 +336,7 @@ private static void ensureNoAwarenessAttributeDecommissioned( } } - private void failDecommissionedNodes(ClusterState state, ActionListener listener) { + private void failDecommissionedNodes(ClusterState state) { DecommissionAttributeMetadata decommissionAttributeMetadata = state.metadata().custom(DecommissionAttributeMetadata.TYPE); // TODO update the status check to DECOMMISSIONING once graceful decommission is implemented assert decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_INIT) @@ -359,6 +359,16 @@ private void failDecommissionedNodes(ClusterState state, ActionListener Date: Wed, 24 Aug 2022 00:50:27 +0530 Subject: [PATCH 032/187] Fix to abdication Signed-off-by: Rishab Nahata --- .../decommission/DecommissionService.java | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 53e73f98e8590..246012d9339bf 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -109,12 +109,12 @@ public void initiateAttributeDecommissioning( validateAwarenessAttribute(decommissionAttribute, getAwarenessAttributes()); this.clusterState = state; logger.info("initiating awareness attribute [{}] decommissioning", decommissionAttribute.toString()); - excludeDecommissionedClusterManagerNodesFromVotingConfig(decommissionAttribute, listener); + excludeDecommissionedClusterManagerNodesFromVotingConfig(decommissionAttribute); + registerDecommissionAttribute(decommissionAttribute, listener); } private void excludeDecommissionedClusterManagerNodesFromVotingConfig( - DecommissionAttribute decommissionAttribute, - final ActionListener listener + DecommissionAttribute decommissionAttribute ) { final Predicate shouldDecommissionPredicate = discoveryNode -> nodeHasDecommissionedAttribute( discoveryNode, @@ -140,7 +140,6 @@ public void handleResponse(AddVotingConfigExclusionsResponse response) { + "proceeding to drain the decommissioned nodes", clusterManagerNodesToBeDecommissioned.toString() ); - registerDecommissionAttribute(decommissionAttribute, listener); } @Override @@ -210,10 +209,14 @@ private void registerDecommissionAttribute( final DecommissionAttribute decommissionAttribute, final ActionListener listener ) { - assert transportService.getLocalNode().isClusterManagerNode() - && !nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute) - : "cannot register decommission attribute, as local node is not master or is going to be decommissioned"; - + logger.info("Node is - " + transportService.getLocalNode()); + if (!transportService.getLocalNode().isClusterManagerNode() + || nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute)) + { + throw new NotClusterManagerException( + "Node [" + transportService.getLocalNode() + "] not eligible to execute decommission request" + ); + } clusterService.submitStateUpdateTask( "put_decommission [" + decommissionAttribute + "]", new ClusterStateUpdateTask(Priority.URGENT) { From 40d718ead183a51577f81eba272fe951da4e359d Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Wed, 24 Aug 2022 01:17:37 +0530 Subject: [PATCH 033/187] Remove cluster state variable from service Signed-off-by: Rishab Nahata --- .../cluster/decommission/DecommissionService.java | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 246012d9339bf..b3fcd9c04a15c 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -62,7 +62,7 @@ * * @opensearch.internal */ -public class DecommissionService implements ClusterStateApplier { +public class DecommissionService { private static final Logger logger = LogManager.getLogger(DecommissionService.class); @@ -70,7 +70,6 @@ public class DecommissionService implements ClusterStateApplier { private final TransportService transportService; private final ThreadPool threadPool; private final DecommissionHelper decommissionHelper; - private ClusterState clusterState; private volatile List awarenessAttributes; @Inject @@ -107,7 +106,6 @@ public void initiateAttributeDecommissioning( ClusterState state ) { validateAwarenessAttribute(decommissionAttribute, getAwarenessAttributes()); - this.clusterState = state; logger.info("initiating awareness attribute [{}] decommissioning", decommissionAttribute.toString()); excludeDecommissionedClusterManagerNodesFromVotingConfig(decommissionAttribute); registerDecommissionAttribute(decommissionAttribute, listener); @@ -121,7 +119,7 @@ private void excludeDecommissionedClusterManagerNodesFromVotingConfig( decommissionAttribute ); List clusterManagerNodesToBeDecommissioned = new ArrayList<>(); - Iterator clusterManagerNodesIter = clusterState.nodes().getClusterManagerNodes().valuesIt(); + Iterator clusterManagerNodesIter = clusterService.state().nodes().getClusterManagerNodes().valuesIt(); while (clusterManagerNodesIter.hasNext()) { final DiscoveryNode node = clusterManagerNodesIter.next(); if (shouldDecommissionPredicate.test(node)) { @@ -209,7 +207,6 @@ private void registerDecommissionAttribute( final DecommissionAttribute decommissionAttribute, final ActionListener listener ) { - logger.info("Node is - " + transportService.getLocalNode()); if (!transportService.getLocalNode().isClusterManagerNode() || nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute)) { @@ -375,9 +372,4 @@ else if (newStatus.equals(DecommissionStatus.DECOMMISSION_SUCCESSFUL)) { private static boolean nodeHasDecommissionedAttribute(DiscoveryNode discoveryNode, DecommissionAttribute decommissionAttribute) { return discoveryNode.getAttributes().get(decommissionAttribute.attributeName()).equals(decommissionAttribute.attributeValue()); } - - @Override - public void applyClusterState(ClusterChangedEvent event) { - clusterState = event.state(); - } } From f481948f2dd24773c0d6dc3754e1291fb7d8a4b0 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Wed, 24 Aug 2022 01:26:01 +0530 Subject: [PATCH 034/187] Log node string Signed-off-by: Rishab Nahata --- .../org/opensearch/cluster/coordination/JoinTaskExecutor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java index c309d2a3e06cc..997ddc3da9ea6 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java @@ -482,7 +482,7 @@ public static void ensureNodeNotDecommissioned(DiscoveryNode node, Metadata meta if (decommissionAttribute != null) { if (node.getAttributes().get(decommissionAttribute.attributeName()).equals(decommissionAttribute.attributeValue())) { throw new NodeDecommissionedException( - "node [{}] has decommissioned attribute [{}].", node.getId(), decommissionAttribute.toString() + "node [{}] has decommissioned attribute [{}].", node.toString(), decommissionAttribute.toString() ); } } From 85ad6dc44c5948281669fd3536f074478055a679 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Wed, 24 Aug 2022 12:11:32 +0530 Subject: [PATCH 035/187] Fix conflict Signed-off-by: Rishab Nahata --- .../decommission/DecommissionService.java | 100 +++++++++++++++--- 1 file changed, 85 insertions(+), 15 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index b3fcd9c04a15c..87081d2ee38b1 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -11,6 +11,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; +import org.opensearch.OpenSearchException; import org.opensearch.action.ActionListener; import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsAction; import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsRequest; @@ -21,6 +22,7 @@ import org.opensearch.cluster.ClusterChangedEvent; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateApplier; +import org.opensearch.cluster.ClusterStateObserver; import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.NotClusterManagerException; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; @@ -35,6 +37,7 @@ import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Settings; +import org.opensearch.common.unit.TimeValue; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportException; import org.opensearch.transport.TransportResponseHandler; @@ -211,7 +214,7 @@ private void registerDecommissionAttribute( || nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute)) { throw new NotClusterManagerException( - "Node [" + transportService.getLocalNode() + "] not eligible to execute decommission request" + "node [" + transportService.getLocalNode().toString() + "] not eligible to execute decommission request" ); } clusterService.submitStateUpdateTask( @@ -251,7 +254,6 @@ public void onFailure(String source, Exception e) { ), e ); - // Do we need a listener here as the transport request will be retried? } else { logger.error( () -> new ParameterizedMessage( @@ -260,7 +262,7 @@ public void onFailure(String source, Exception e) { ), e ); - updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_FAILED); +// updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_FAILED); listener.onFailure(e); } } @@ -270,19 +272,32 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS assert !newState.equals(oldState) : "no update in cluster state after initiating decommission request."; // Do we attach a listener here with failed acknowledgement to the request? listener.onResponse(new ClusterStateUpdateResponse(true)); - initiateGracefulDecommission(newState); + initiateGracefulDecommission(); } } ); } - private void initiateGracefulDecommission(ClusterState clusterState) { - failDecommissionedNodes(clusterState); + private void initiateGracefulDecommission() { + ActionListener listener = new ActionListener() { + @Override + public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { + failDecommissionedNodes(clusterService.state()); + } + + @Override + public void onFailure(Exception e) { + + } + }; + updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_IN_PROGRESS, listener); + //TODO - code for graceful decommission } // To Do - Can we add a consumer here such that whenever this succeeds we call the next method in on cluster state processed private void updateMetadataWithDecommissionStatus( - DecommissionStatus decommissionStatus + DecommissionStatus decommissionStatus, + ActionListener listener ) { clusterService.submitStateUpdateTask( decommissionStatus.status(), @@ -311,6 +326,13 @@ public void onFailure(String source, Exception e) { e ); } + + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + listener.onResponse(new ClusterStateUpdateResponse(true)); + } + + } ); } @@ -338,25 +360,73 @@ private static void ensureNoAwarenessAttributeDecommissioned( private void failDecommissionedNodes(ClusterState state) { DecommissionAttributeMetadata decommissionAttributeMetadata = state.metadata().custom(DecommissionAttributeMetadata.TYPE); - // TODO update the status check to DECOMMISSIONING once graceful decommission is implemented - assert decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_INIT) + assert decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_IN_PROGRESS) : "unexpected status encountered while decommissioning nodes"; DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute(); - List nodesToBeDecommissioned = new ArrayList<>(); + + decommissionHelper.handleNodesDecommissionRequest( + nodesWithDecommissionAttribute(state, decommissionAttribute), + "nodes-decommissioned" + ); + + final ClusterStateObserver observer = new ClusterStateObserver( + clusterService, + TimeValue.timeValueSeconds(30L), + logger, + threadPool.getThreadContext() + ); + + final Predicate allDecommissionedNodesRemoved = clusterState -> { + List nodesWithDecommissionAttribute = nodesWithDecommissionAttribute(clusterState, decommissionAttribute); + return nodesWithDecommissionAttribute.size() == 0; + }; + ActionListener statusUpdateListener = new ActionListener() { + @Override + public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { + logger.info( + "successfully updated decommission status" + ); + } + + @Override + public void onFailure(Exception e) { + + } + }; + + observer.waitForNextChange(new ClusterStateObserver.Listener() { + @Override + public void onNewClusterState(ClusterState state) { + clearVotingConfigAfterSuccessfulDecommission(); + updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_SUCCESSFUL, statusUpdateListener); + } + + @Override + public void onClusterServiceClose() { + } + + @Override + public void onTimeout(TimeValue timeout) { + clearVotingConfigAfterSuccessfulDecommission(); + updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_FAILED, statusUpdateListener); + } + }, allDecommissionedNodesRemoved, TimeValue.timeValueSeconds(30L)); + } + + private List nodesWithDecommissionAttribute(ClusterState clusterState, DecommissionAttribute decommissionAttribute) { + List nodesWithDecommissionAttribute = new ArrayList<>(); final Predicate shouldRemoveNodePredicate = discoveryNode -> nodeHasDecommissionedAttribute( discoveryNode, decommissionAttribute ); - Iterator nodesIter = state.nodes().getNodes().valuesIt(); + Iterator nodesIter = clusterState.nodes().getNodes().valuesIt(); while (nodesIter.hasNext()) { final DiscoveryNode node = nodesIter.next(); if (shouldRemoveNodePredicate.test(node)) { - nodesToBeDecommissioned.add(node); + nodesWithDecommissionAttribute.add(node); } } - // TODO - check for response from decommission request and then clear voting config? - decommissionHelper.handleNodesDecommissionRequest(nodesToBeDecommissioned, "nodes-decommissioned"); - clearVotingConfigAfterSuccessfulDecommission(); + return nodesWithDecommissionAttribute; } private static boolean assertIncrementalStatusOrFailed(DecommissionStatus oldStatus, DecommissionStatus newStatus) { From 6c5bd01d5323ccb0223683ac003d7b6f90fd2e3e Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Wed, 24 Aug 2022 15:50:23 +0530 Subject: [PATCH 036/187] Changes in Service Signed-off-by: Rishab Nahata --- .../decommission/DecommissionService.java | 207 +++++++++--------- 1 file changed, 106 insertions(+), 101 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 87081d2ee38b1..b97cca12c5006 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -11,7 +11,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.opensearch.OpenSearchException; import org.opensearch.action.ActionListener; import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsAction; import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsRequest; @@ -19,9 +18,7 @@ import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsAction; import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsRequest; import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsResponse; -import org.opensearch.cluster.ClusterChangedEvent; import org.opensearch.cluster.ClusterState; -import org.opensearch.cluster.ClusterStateApplier; import org.opensearch.cluster.ClusterStateObserver; import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.NotClusterManagerException; @@ -54,14 +51,14 @@ *

* Whenever a cluster manager initiates operation to decommission an awareness attribute, * the service makes the best attempt to perform the following task - - *

- * 1. Remove cluster-manager eligible nodes from voting config [TODO - checks to avoid quorum loss scenarios] - * 2. Initiates nodes decommissioning by adding custom metadata with the attribute and state as {@link DecommissionStatus#DECOMMISSION_INIT} - * 3. Triggers weigh away for nodes having given awareness attribute to drain. This marks the decommission status as {@link DecommissionStatus#DECOMMISSION_IN_PROGRESS} - * 4. Once weighed away, the service triggers nodes decommission - * 5. Once the decommission is successful, the service clears the voting config and marks the status as {@link DecommissionStatus#DECOMMISSION_SUCCESSFUL} - * 6. If service fails at any step, it would mark the status as {@link DecommissionStatus#DECOMMISSION_FAILED} - *

+ *
    + *
  • Remove cluster-manager eligible nodes from voting config [TODO - checks to avoid quorum loss scenarios]
  • + *
  • Initiates nodes decommissioning by adding custom metadata with the attribute and state as {@link DecommissionStatus#DECOMMISSION_INIT}
  • + *
  • Triggers weigh away for nodes having given awareness attribute to drain. This marks the decommission status as {@link DecommissionStatus#DECOMMISSION_IN_PROGRESS}
  • + *
  • Once weighed away, the service triggers nodes decommission
  • + *
  • Once the decommission is successful, the service clears the voting config and marks the status as {@link DecommissionStatus#DECOMMISSION_SUCCESSFUL}
  • + *
  • If service fails at any step, it would mark the status as {@link DecommissionStatus#DECOMMISSION_FAILED}
  • + *
* * @opensearch.internal */ @@ -114,9 +111,7 @@ public void initiateAttributeDecommissioning( registerDecommissionAttribute(decommissionAttribute, listener); } - private void excludeDecommissionedClusterManagerNodesFromVotingConfig( - DecommissionAttribute decommissionAttribute - ) { + private void excludeDecommissionedClusterManagerNodesFromVotingConfig(DecommissionAttribute decommissionAttribute) { final Predicate shouldDecommissionPredicate = discoveryNode -> nodeHasDecommissionedAttribute( discoveryNode, decommissionAttribute @@ -196,12 +191,12 @@ public ClearVotingConfigExclusionsResponse read(StreamInput in) throws IOExcepti } /** - * Registers new decommissioned attribute metadata in the cluster state + * Registers new decommissioned attribute metadata in the cluster state with {@link DecommissionStatus#DECOMMISSION_INIT} *

* This method can be only called on the cluster-manager node. It tries to create a new decommissioned attribute on the master * and if it was successful it adds new decommissioned attribute to cluster metadata. *

- * This method should only be called once the eligible cluster manager node having decommissioned attribute is abdicated + * This method ensures that request is performed only on eligible cluster manager node * * @param decommissionAttribute register decommission attribute in the metadata request * @param listener register decommission listener @@ -210,11 +205,10 @@ private void registerDecommissionAttribute( final DecommissionAttribute decommissionAttribute, final ActionListener listener ) { - if (!transportService.getLocalNode().isClusterManagerNode() - || nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute)) + if (!transportService.getLocalNode().isClusterManagerNode() || nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute)) { throw new NotClusterManagerException( - "node [" + transportService.getLocalNode().toString() + "] not eligible to execute decommission request" + "node [" + transportService.getLocalNode().toString() + "] not eligible to execute decommission request. Will retry until timeout." ); } clusterService.submitStateUpdateTask( @@ -262,15 +256,15 @@ public void onFailure(String source, Exception e) { ), e ); -// updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_FAILED); listener.onFailure(e); } } @Override public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { - assert !newState.equals(oldState) : "no update in cluster state after initiating decommission request."; - // Do we attach a listener here with failed acknowledgement to the request? + DecommissionAttributeMetadata decommissionAttributeMetadata = newState.metadata().custom(DecommissionAttributeMetadata.TYPE); + assert decommissionAttribute.equals(decommissionAttributeMetadata.decommissionAttribute()); + assert DecommissionStatus.DECOMMISSION_INIT.equals(decommissionAttributeMetadata.status()); listener.onResponse(new ClusterStateUpdateResponse(true)); initiateGracefulDecommission(); } @@ -279,22 +273,88 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS } private void initiateGracefulDecommission() { - ActionListener listener = new ActionListener() { + // maybe create a supplier for status update listener? + ActionListener listener = new ActionListener<>() { @Override public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { + logger.info("updated decommission status to [{}], weighing away awareness attribute for graceful shutdown", + DecommissionStatus.DECOMMISSION_IN_PROGRESS); failDecommissionedNodes(clusterService.state()); } @Override public void onFailure(Exception e) { - + logger.error( + () -> new ParameterizedMessage( "failed to update decommission status to [{}], will not proceed with decommission" + , DecommissionStatus.DECOMMISSION_IN_PROGRESS), + e + ); } }; updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_IN_PROGRESS, listener); //TODO - code for graceful decommission } - // To Do - Can we add a consumer here such that whenever this succeeds we call the next method in on cluster state processed + private void failDecommissionedNodes(ClusterState state) { + DecommissionAttributeMetadata decommissionAttributeMetadata = state.metadata().custom(DecommissionAttributeMetadata.TYPE); + assert decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_IN_PROGRESS) + : "unexpected status encountered while decommissioning nodes"; + DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute(); + + ActionListener statusUpdateListener = new ActionListener<>() { + @Override + public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { + logger.info( + "successfully updated decommission status" + ); + } + + @Override + public void onFailure(Exception e) { + logger.error("failed to update the decommission status"); + } + }; + + // execute decommissioning + decommissionHelper.handleNodesDecommissionRequest( + nodesWithDecommissionAttribute(state, decommissionAttribute), + "nodes-decommissioned" + ); + + final ClusterStateObserver observer = new ClusterStateObserver( + clusterService, + TimeValue.timeValueSeconds(30L), // should this be a setting? + logger, + threadPool.getThreadContext() + ); + + final Predicate allDecommissionedNodesRemoved = clusterState -> { + List nodesWithDecommissionAttribute = nodesWithDecommissionAttribute(clusterState, decommissionAttribute); + return nodesWithDecommissionAttribute.size() == 0; + }; + + observer.waitForNextChange(new ClusterStateObserver.Listener() { + @Override + public void onNewClusterState(ClusterState state) { + logger.info("successfully removed all decommissioned nodes from the cluster"); + clearVotingConfigAfterSuccessfulDecommission(); + updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_SUCCESSFUL, statusUpdateListener); + } + + @Override + public void onClusterServiceClose() { + logger.debug("cluster service closed while waiting for removal of decommissioned nodes."); + } + + @Override + public void onTimeout(TimeValue timeout) { + logger.info("timed out while waiting for removal of decommissioned nodes"); + clearVotingConfigAfterSuccessfulDecommission(); + updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_FAILED, statusUpdateListener); + } + }, allDecommissionedNodesRemoved); + } + private void updateMetadataWithDecommissionStatus( DecommissionStatus decommissionStatus, ActionListener listener @@ -337,82 +397,6 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS ); } - private static void validateAwarenessAttribute(final DecommissionAttribute decommissionAttribute, List awarenessAttributes) { - if (!awarenessAttributes.contains(decommissionAttribute.attributeName())) { - throw new DecommissionFailedException(decommissionAttribute, "invalid awareness attribute requested for decommissioning"); - } - // TODO - should attribute value be part of force zone values? If yes, read setting and throw exception if not found - } - - private static void ensureNoAwarenessAttributeDecommissioned( - DecommissionAttributeMetadata decommissionAttributeMetadata, - DecommissionAttribute decommissionAttribute - ) { - // If the previous decommission request failed, we will allow the request to pass this check - if (decommissionAttributeMetadata != null - && !decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_FAILED)) { - throw new DecommissionFailedException( - decommissionAttribute, - "one awareness attribute already decommissioned, recommission before triggering another decommission" - ); - } - } - - private void failDecommissionedNodes(ClusterState state) { - DecommissionAttributeMetadata decommissionAttributeMetadata = state.metadata().custom(DecommissionAttributeMetadata.TYPE); - assert decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_IN_PROGRESS) - : "unexpected status encountered while decommissioning nodes"; - DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute(); - - decommissionHelper.handleNodesDecommissionRequest( - nodesWithDecommissionAttribute(state, decommissionAttribute), - "nodes-decommissioned" - ); - - final ClusterStateObserver observer = new ClusterStateObserver( - clusterService, - TimeValue.timeValueSeconds(30L), - logger, - threadPool.getThreadContext() - ); - - final Predicate allDecommissionedNodesRemoved = clusterState -> { - List nodesWithDecommissionAttribute = nodesWithDecommissionAttribute(clusterState, decommissionAttribute); - return nodesWithDecommissionAttribute.size() == 0; - }; - ActionListener statusUpdateListener = new ActionListener() { - @Override - public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { - logger.info( - "successfully updated decommission status" - ); - } - - @Override - public void onFailure(Exception e) { - - } - }; - - observer.waitForNextChange(new ClusterStateObserver.Listener() { - @Override - public void onNewClusterState(ClusterState state) { - clearVotingConfigAfterSuccessfulDecommission(); - updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_SUCCESSFUL, statusUpdateListener); - } - - @Override - public void onClusterServiceClose() { - } - - @Override - public void onTimeout(TimeValue timeout) { - clearVotingConfigAfterSuccessfulDecommission(); - updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_FAILED, statusUpdateListener); - } - }, allDecommissionedNodesRemoved, TimeValue.timeValueSeconds(30L)); - } - private List nodesWithDecommissionAttribute(ClusterState clusterState, DecommissionAttribute decommissionAttribute) { List nodesWithDecommissionAttribute = new ArrayList<>(); final Predicate shouldRemoveNodePredicate = discoveryNode -> nodeHasDecommissionedAttribute( @@ -442,4 +426,25 @@ else if (newStatus.equals(DecommissionStatus.DECOMMISSION_SUCCESSFUL)) { private static boolean nodeHasDecommissionedAttribute(DiscoveryNode discoveryNode, DecommissionAttribute decommissionAttribute) { return discoveryNode.getAttributes().get(decommissionAttribute.attributeName()).equals(decommissionAttribute.attributeValue()); } + + private static void validateAwarenessAttribute(final DecommissionAttribute decommissionAttribute, List awarenessAttributes) { + if (!awarenessAttributes.contains(decommissionAttribute.attributeName())) { + throw new DecommissionFailedException(decommissionAttribute, "invalid awareness attribute requested for decommissioning"); + } + // TODO - should attribute value be part of force zone values? If yes, read setting and throw exception if not found + } + + private static void ensureNoAwarenessAttributeDecommissioned( + DecommissionAttributeMetadata decommissionAttributeMetadata, + DecommissionAttribute decommissionAttribute + ) { + // If the previous decommission request failed, we will allow the request to pass this check + if (decommissionAttributeMetadata != null + && !decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_FAILED)) { + throw new DecommissionFailedException( + decommissionAttribute, + "one awareness attribute already decommissioned, recommission before triggering another decommission" + ); + } + } } From cb703f19163501704072e2fc7963ecfeeff5e842 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Wed, 24 Aug 2022 15:55:34 +0530 Subject: [PATCH 037/187] Fix spotless check Signed-off-by: Rishab Nahata --- .../org/opensearch/OpenSearchException.java | 1 - .../coordination/JoinTaskExecutor.java | 4 +- .../decommission/DecommissionAttribute.java | 1 - .../decommission/DecommissionService.java | 90 +++++++++---------- 4 files changed, 44 insertions(+), 52 deletions(-) diff --git a/server/src/main/java/org/opensearch/OpenSearchException.java b/server/src/main/java/org/opensearch/OpenSearchException.java index 5ae83c9df70d3..b1e9a27b98416 100644 --- a/server/src/main/java/org/opensearch/OpenSearchException.java +++ b/server/src/main/java/org/opensearch/OpenSearchException.java @@ -69,7 +69,6 @@ import static java.util.Collections.unmodifiableMap; import static org.opensearch.Version.V_2_1_0; import static org.opensearch.Version.V_2_3_0; -import static org.opensearch.Version.V_3_0_0; import static org.opensearch.cluster.metadata.IndexMetadata.INDEX_UUID_NA_VALUE; import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken; import static org.opensearch.common.xcontent.XContentParserUtils.ensureFieldName; diff --git a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java index 997ddc3da9ea6..9825bebb63dd6 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java @@ -482,7 +482,9 @@ public static void ensureNodeNotDecommissioned(DiscoveryNode node, Metadata meta if (decommissionAttribute != null) { if (node.getAttributes().get(decommissionAttribute.attributeName()).equals(decommissionAttribute.attributeValue())) { throw new NodeDecommissionedException( - "node [{}] has decommissioned attribute [{}].", node.toString(), decommissionAttribute.toString() + "node [{}] has decommissioned attribute [{}].", + node.toString(), + decommissionAttribute.toString() ); } } diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java index 1a30b59d5a60e..bf2487a1a0e18 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java @@ -8,7 +8,6 @@ package org.opensearch.cluster.decommission; -import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.io.stream.Writeable; diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index b97cca12c5006..139b241ea00a4 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -205,10 +205,12 @@ private void registerDecommissionAttribute( final DecommissionAttribute decommissionAttribute, final ActionListener listener ) { - if (!transportService.getLocalNode().isClusterManagerNode() || nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute)) - { + if (!transportService.getLocalNode().isClusterManagerNode() + || nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute)) { throw new NotClusterManagerException( - "node [" + transportService.getLocalNode().toString() + "] not eligible to execute decommission request. Will retry until timeout." + "node [" + + transportService.getLocalNode().toString() + + "] not eligible to execute decommission request. Will retry until timeout." ); } clusterService.submitStateUpdateTask( @@ -234,9 +236,7 @@ public ClusterState execute(ClusterState currentState) { public void onFailure(String source, Exception e) { if (e instanceof DecommissionFailedException) { logger.error( - () -> new ParameterizedMessage( - "failed to decommission attribute [{}]", - decommissionAttribute.toString()), + () -> new ParameterizedMessage("failed to decommission attribute [{}]", decommissionAttribute.toString()), e ); listener.onFailure(e); @@ -262,7 +262,8 @@ public void onFailure(String source, Exception e) { @Override public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { - DecommissionAttributeMetadata decommissionAttributeMetadata = newState.metadata().custom(DecommissionAttributeMetadata.TYPE); + DecommissionAttributeMetadata decommissionAttributeMetadata = newState.metadata() + .custom(DecommissionAttributeMetadata.TYPE); assert decommissionAttribute.equals(decommissionAttributeMetadata.decommissionAttribute()); assert DecommissionStatus.DECOMMISSION_INIT.equals(decommissionAttributeMetadata.status()); listener.onResponse(new ClusterStateUpdateResponse(true)); @@ -277,22 +278,26 @@ private void initiateGracefulDecommission() { ActionListener listener = new ActionListener<>() { @Override public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { - logger.info("updated decommission status to [{}], weighing away awareness attribute for graceful shutdown", - DecommissionStatus.DECOMMISSION_IN_PROGRESS); + logger.info( + "updated decommission status to [{}], weighing away awareness attribute for graceful shutdown", + DecommissionStatus.DECOMMISSION_IN_PROGRESS + ); failDecommissionedNodes(clusterService.state()); } @Override public void onFailure(Exception e) { logger.error( - () -> new ParameterizedMessage( "failed to update decommission status to [{}], will not proceed with decommission" - , DecommissionStatus.DECOMMISSION_IN_PROGRESS), + () -> new ParameterizedMessage( + "failed to update decommission status to [{}], will not proceed with decommission", + DecommissionStatus.DECOMMISSION_IN_PROGRESS + ), e ); } }; updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_IN_PROGRESS, listener); - //TODO - code for graceful decommission + // TODO - code for graceful decommission } private void failDecommissionedNodes(ClusterState state) { @@ -304,9 +309,7 @@ private void failDecommissionedNodes(ClusterState state) { ActionListener statusUpdateListener = new ActionListener<>() { @Override public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { - logger.info( - "successfully updated decommission status" - ); + logger.info("successfully updated decommission status"); } @Override @@ -359,42 +362,31 @@ private void updateMetadataWithDecommissionStatus( DecommissionStatus decommissionStatus, ActionListener listener ) { - clusterService.submitStateUpdateTask( - decommissionStatus.status(), - new ClusterStateUpdateTask(Priority.URGENT) { - @Override - public ClusterState execute(ClusterState currentState) throws Exception { - Metadata metadata = currentState.metadata(); - DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.custom(DecommissionAttributeMetadata.TYPE); - assert decommissionAttributeMetadata != null - && decommissionAttributeMetadata.decommissionAttribute() != null - : "failed to update status for decommission. metadata doesn't exist or invalid"; - assert assertIncrementalStatusOrFailed(decommissionAttributeMetadata.status(), decommissionStatus); - Metadata.Builder mdBuilder = Metadata.builder(metadata); - DecommissionAttributeMetadata newMetadata = decommissionAttributeMetadata.withUpdatedStatus(decommissionStatus); - mdBuilder.putCustom(DecommissionAttributeMetadata.TYPE, newMetadata); - return ClusterState.builder(currentState).metadata(mdBuilder).build(); - } - - @Override - public void onFailure(String source, Exception e) { - logger.error( - () -> new ParameterizedMessage( - "failed to mark status as [{}]", - decommissionStatus.status() - ), - e - ); - } - - @Override - public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { - listener.onResponse(new ClusterStateUpdateResponse(true)); - } + clusterService.submitStateUpdateTask(decommissionStatus.status(), new ClusterStateUpdateTask(Priority.URGENT) { + @Override + public ClusterState execute(ClusterState currentState) throws Exception { + Metadata metadata = currentState.metadata(); + DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.custom(DecommissionAttributeMetadata.TYPE); + assert decommissionAttributeMetadata != null && decommissionAttributeMetadata.decommissionAttribute() != null + : "failed to update status for decommission. metadata doesn't exist or invalid"; + assert assertIncrementalStatusOrFailed(decommissionAttributeMetadata.status(), decommissionStatus); + Metadata.Builder mdBuilder = Metadata.builder(metadata); + DecommissionAttributeMetadata newMetadata = decommissionAttributeMetadata.withUpdatedStatus(decommissionStatus); + mdBuilder.putCustom(DecommissionAttributeMetadata.TYPE, newMetadata); + return ClusterState.builder(currentState).metadata(mdBuilder).build(); + } + @Override + public void onFailure(String source, Exception e) { + logger.error(() -> new ParameterizedMessage("failed to mark status as [{}]", decommissionStatus.status()), e); + } + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + listener.onResponse(new ClusterStateUpdateResponse(true)); } - ); + + }); } private List nodesWithDecommissionAttribute(ClusterState clusterState, DecommissionAttribute decommissionAttribute) { @@ -410,7 +402,7 @@ private List nodesWithDecommissionAttribute(ClusterState clusterS nodesWithDecommissionAttribute.add(node); } } - return nodesWithDecommissionAttribute; + return nodesWithDecommissionAttribute; } private static boolean assertIncrementalStatusOrFailed(DecommissionStatus oldStatus, DecommissionStatus newStatus) { From 13ad6176958e1443c235c59925be46499ca4d5a9 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Wed, 24 Aug 2022 16:14:49 +0530 Subject: [PATCH 038/187] Update the join validator for decommissioned attribute Signed-off-by: Rishab Nahata --- .../org/opensearch/cluster/coordination/JoinTaskExecutor.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java index 9825bebb63dd6..2eba89c72ae2b 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java @@ -40,6 +40,7 @@ import org.opensearch.cluster.NotClusterManagerException; import org.opensearch.cluster.block.ClusterBlocks; import org.opensearch.cluster.decommission.DecommissionAttribute; +import org.opensearch.cluster.decommission.DecommissionStatus; import org.opensearch.cluster.decommission.NodeDecommissionedException; import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; import org.opensearch.cluster.metadata.IndexMetadata; @@ -479,7 +480,8 @@ public static void ensureNodeNotDecommissioned(DiscoveryNode node, Metadata meta DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.custom(DecommissionAttributeMetadata.TYPE); if (decommissionAttributeMetadata != null) { DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute(); - if (decommissionAttribute != null) { + if (decommissionAttribute != null && decommissionAttributeMetadata.status() != null) { + if(decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_FAILED)) return; if (node.getAttributes().get(decommissionAttribute.attributeName()).equals(decommissionAttribute.attributeValue())) { throw new NodeDecommissionedException( "node [{}] has decommissioned attribute [{}].", From f26ea2c7a6bec3136d63bde280c7dd128fea454c Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Wed, 24 Aug 2022 20:22:38 +0530 Subject: [PATCH 039/187] Add UTs for metadata Signed-off-by: Rishab Nahata --- .../org/opensearch/OpenSearchException.java | 1 + .../DecommissionAttributeMetadata.java | 3 +- .../ExceptionSerializationTests.java | 4 + ...onAttributeMetadataSerializationTests.java | 85 +++++++++++++++++++ .../DecommissionAttributeMetadataTests.java | 51 +++++++++++ ...missionAttributeMetadataXContentTests.java | 37 ++++++++ 6 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataSerializationTests.java create mode 100644 server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataTests.java create mode 100644 server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataXContentTests.java diff --git a/server/src/main/java/org/opensearch/OpenSearchException.java b/server/src/main/java/org/opensearch/OpenSearchException.java index b1e9a27b98416..5ae83c9df70d3 100644 --- a/server/src/main/java/org/opensearch/OpenSearchException.java +++ b/server/src/main/java/org/opensearch/OpenSearchException.java @@ -69,6 +69,7 @@ import static java.util.Collections.unmodifiableMap; import static org.opensearch.Version.V_2_1_0; import static org.opensearch.Version.V_2_3_0; +import static org.opensearch.Version.V_3_0_0; import static org.opensearch.cluster.metadata.IndexMetadata.INDEX_UUID_NA_VALUE; import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken; import static org.opensearch.common.xcontent.XContentParserUtils.ensureFieldName; diff --git a/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java b/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java index c6252e9981810..2034ab34e25c3 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java @@ -137,7 +137,7 @@ public static DecommissionAttributeMetadata fromXContent(XContentParser parser) XContentParser.Token token; DecommissionAttribute decommissionAttribute = null; DecommissionStatus status = null; - if ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) { String currentFieldName = parser.currentName(); if (attributeType.equals(currentFieldName)) { @@ -162,6 +162,7 @@ public static DecommissionAttributeMetadata fromXContent(XContentParser parser) ); } decommissionAttribute = new DecommissionAttribute(fieldName, value); + token = parser.nextToken(); } else { throw new OpenSearchParseException("failed to parse attribute type [{}], unexpected type", attributeType); } diff --git a/server/src/test/java/org/opensearch/ExceptionSerializationTests.java b/server/src/test/java/org/opensearch/ExceptionSerializationTests.java index 26b0ce7e9e20c..6516cc80c7929 100644 --- a/server/src/test/java/org/opensearch/ExceptionSerializationTests.java +++ b/server/src/test/java/org/opensearch/ExceptionSerializationTests.java @@ -49,6 +49,8 @@ import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.coordination.CoordinationStateRejectedException; import org.opensearch.cluster.coordination.NoClusterManagerBlockService; +import org.opensearch.cluster.decommission.DecommissionFailedException; +import org.opensearch.cluster.decommission.NodeDecommissionedException; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.routing.IllegalShardRoutingStateException; import org.opensearch.cluster.routing.ShardRouting; @@ -860,6 +862,8 @@ public void testIds() { ids.put(160, NoSeedNodeLeftException.class); ids.put(161, ReplicationFailedException.class); ids.put(162, PrimaryShardClosedException.class); + ids.put(163, DecommissionFailedException.class); + ids.put(164, NodeDecommissionedException.class); Map, Integer> reverse = new HashMap<>(); for (Map.Entry> entry : ids.entrySet()) { diff --git a/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataSerializationTests.java b/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataSerializationTests.java new file mode 100644 index 0000000000000..d81c05b8e8da0 --- /dev/null +++ b/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataSerializationTests.java @@ -0,0 +1,85 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.metadata; + +import org.opensearch.cluster.ClusterModule; +import org.opensearch.cluster.Diff; +import org.opensearch.cluster.decommission.DecommissionAttribute; +import org.opensearch.cluster.decommission.DecommissionStatus; +import org.opensearch.common.io.stream.NamedWriteableRegistry; +import org.opensearch.common.io.stream.Writeable; +import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.test.AbstractDiffableSerializationTestCase; + +import java.io.IOException; + +public class DecommissionAttributeMetadataSerializationTests extends AbstractDiffableSerializationTestCase { + + @Override + protected Writeable.Reader instanceReader() { + return DecommissionAttributeMetadata::new; + } + + @Override + protected Metadata.Custom createTestInstance() { + String attributeName = randomAlphaOfLength(6); + String attributeValue = randomAlphaOfLength(6); + DecommissionAttribute decommissionAttribute = new DecommissionAttribute(attributeName, attributeValue); + DecommissionStatus decommissionStatus = randomFrom(DecommissionStatus.values()); + return new DecommissionAttributeMetadata(decommissionAttribute, decommissionStatus); + } + + @Override + protected Metadata.Custom mutateInstance(Metadata.Custom instance) { + return randomValueOtherThan(instance, this::createTestInstance); + } + + @Override + protected Metadata.Custom makeTestChanges(Metadata.Custom testInstance) { + DecommissionAttributeMetadata decommissionAttributeMetadata = (DecommissionAttributeMetadata) testInstance; + DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute(); + String attributeName = decommissionAttribute.attributeName(); + String attributeValue = decommissionAttribute.attributeValue(); + DecommissionStatus decommissionStatus = decommissionAttributeMetadata.status(); + if (randomBoolean()) { + decommissionStatus = randomFrom(DecommissionStatus.values()); + } + if (randomBoolean()) { + attributeName = randomAlphaOfLength(6); + } + if(randomBoolean()) { + attributeValue = randomAlphaOfLength(6); + } + return new DecommissionAttributeMetadata( + new DecommissionAttribute(attributeName, attributeValue), + decommissionStatus + ); + } + + @Override + protected Writeable.Reader> diffReader() { + return DecommissionAttributeMetadata::readDiffFrom; + } + + @Override + protected NamedWriteableRegistry getNamedWriteableRegistry() { + return new NamedWriteableRegistry(ClusterModule.getNamedWriteables()); + } + + @Override + protected Metadata.Custom doParseInstance(XContentParser parser) throws IOException { + assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken()); + DecommissionAttributeMetadata decommissionAttributeMetadata = DecommissionAttributeMetadata.fromXContent(parser); + assertEquals(XContentParser.Token.END_OBJECT, parser.currentToken()); + return new DecommissionAttributeMetadata( + decommissionAttributeMetadata.decommissionAttribute(), + decommissionAttributeMetadata.status() + ); + } +} diff --git a/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataTests.java b/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataTests.java new file mode 100644 index 0000000000000..bff57daef6109 --- /dev/null +++ b/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataTests.java @@ -0,0 +1,51 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.metadata; + +import org.opensearch.cluster.decommission.DecommissionAttribute; +import org.opensearch.cluster.decommission.DecommissionStatus; +import org.opensearch.common.io.stream.NamedWriteableRegistry; +import org.opensearch.test.AbstractNamedWriteableTestCase; + +import java.io.IOException; +import java.util.Collections; + +public class DecommissionAttributeMetadataTests extends AbstractNamedWriteableTestCase { + @Override + protected DecommissionAttributeMetadata createTestInstance() { + String attributeName = randomAlphaOfLength(6); + String attributeValue = randomAlphaOfLength(6); + DecommissionAttribute decommissionAttribute = new DecommissionAttribute(attributeName, attributeValue); + DecommissionStatus decommissionStatus = randomFrom(DecommissionStatus.values()); + return new DecommissionAttributeMetadata(decommissionAttribute, decommissionStatus); + } + + @Override + protected DecommissionAttributeMetadata mutateInstance(DecommissionAttributeMetadata instance) throws IOException { + return randomValueOtherThan(instance, this::createTestInstance); + } + + @Override + protected NamedWriteableRegistry getNamedWriteableRegistry() { + return new NamedWriteableRegistry( + Collections.singletonList( + new NamedWriteableRegistry.Entry( + DecommissionAttributeMetadata.class, + DecommissionAttributeMetadata.TYPE, + DecommissionAttributeMetadata::new + ) + ) + ); + } + + @Override + protected Class categoryClass() { + return DecommissionAttributeMetadata.class; + } +} diff --git a/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataXContentTests.java b/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataXContentTests.java new file mode 100644 index 0000000000000..c632839acd4ca --- /dev/null +++ b/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataXContentTests.java @@ -0,0 +1,37 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.metadata; + +import org.opensearch.cluster.decommission.DecommissionAttribute; +import org.opensearch.cluster.decommission.DecommissionStatus; +import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.test.AbstractXContentTestCase; + +import java.io.IOException; + +public class DecommissionAttributeMetadataXContentTests extends AbstractXContentTestCase { + @Override + protected DecommissionAttributeMetadata createTestInstance() { + String attributeName = randomAlphaOfLength(6); + String attributeValue = randomAlphaOfLength(6); + DecommissionAttribute decommissionAttribute = new DecommissionAttribute(attributeName, attributeValue); + DecommissionStatus decommissionStatus = randomFrom(DecommissionStatus.values()); + return new DecommissionAttributeMetadata(decommissionAttribute, decommissionStatus); + } + + @Override + protected DecommissionAttributeMetadata doParseInstance(XContentParser parser) throws IOException { + return DecommissionAttributeMetadata.fromXContent(parser); + } + + @Override + protected boolean supportsUnknownFields() { + return false; + } +} From 208cb33ffc8a148dac74423629bfb4aa89c63855 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Thu, 25 Aug 2022 00:45:09 +0530 Subject: [PATCH 040/187] Add UTs for JoinTaskExecutor changes Signed-off-by: Rishab Nahata --- .../coordination/JoinTaskExecutor.java | 13 ++- .../coordination/JoinTaskExecutorTests.java | 86 +++++++++++++++++++ 2 files changed, 96 insertions(+), 3 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java index 2eba89c72ae2b..469c0b786056f 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java @@ -480,9 +480,16 @@ public static void ensureNodeNotDecommissioned(DiscoveryNode node, Metadata meta DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.custom(DecommissionAttributeMetadata.TYPE); if (decommissionAttributeMetadata != null) { DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute(); - if (decommissionAttribute != null && decommissionAttributeMetadata.status() != null) { - if(decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_FAILED)) return; - if (node.getAttributes().get(decommissionAttribute.attributeName()).equals(decommissionAttribute.attributeValue())) { + DecommissionStatus status = decommissionAttributeMetadata.status(); + if (decommissionAttribute != null && status != null) { + // We will let the node join the cluster if the current status is INIT or FAILED + if(status.equals(DecommissionStatus.DECOMMISSION_FAILED) || status.equals(DecommissionStatus.DECOMMISSION_INIT)) return; + if (node.getAttributes().get(decommissionAttribute.attributeName()).equals(decommissionAttribute.attributeValue()) + && ( + status.equals(DecommissionStatus.DECOMMISSION_IN_PROGRESS) + || status.equals(DecommissionStatus.DECOMMISSION_SUCCESSFUL + ) + )) { throw new NodeDecommissionedException( "node [{}] has decommissioned attribute [{}].", node.toString(), diff --git a/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java b/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java index 02e502e762561..a3c3b9a9b9c7b 100644 --- a/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java +++ b/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java @@ -36,9 +36,15 @@ import org.opensearch.cluster.ClusterName; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateTaskExecutor; +import org.opensearch.cluster.decommission.DecommissionAttribute; +import org.opensearch.cluster.decommission.DecommissionFailedException; +import org.opensearch.cluster.decommission.DecommissionStatus; +import org.opensearch.cluster.decommission.NodeDecommissionedException; +import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.node.DiscoveryNodeRole; import org.opensearch.cluster.node.DiscoveryNodes; import org.opensearch.cluster.routing.RerouteService; import org.opensearch.cluster.routing.allocation.AllocationService; @@ -48,8 +54,11 @@ import org.opensearch.test.OpenSearchTestCase; import org.opensearch.test.VersionUtils; +import java.util.Collections; import java.util.HashSet; +import java.util.Map; +import static java.util.Collections.emptyMap; import static org.hamcrest.Matchers.is; import static org.opensearch.test.VersionUtils.allVersions; import static org.opensearch.test.VersionUtils.maxCompatibleVersion; @@ -216,4 +225,81 @@ public void testIsBecomeClusterManagerTask() { JoinTaskExecutor.Task joinTaskOfClusterManager = JoinTaskExecutor.newBecomeClusterManagerTask(); assertThat(joinTaskOfClusterManager.isBecomeClusterManagerTask(), is(true)); } + + public void testJoinClusterWithNoDecommission() { + Settings.builder().build(); + Metadata.Builder metaBuilder = Metadata.builder(); + Metadata metadata = metaBuilder.build(); + DiscoveryNode discoveryNode = newDiscoveryNode(Collections.singletonMap("zone", "zone-2")); + JoinTaskExecutor.ensureNodeNotDecommissioned(discoveryNode, metadata); + } + + public void testPreventJoinClusterWithDecommission() { + Settings.builder().build(); + DecommissionAttribute decommissionAttribute = new DecommissionAttribute("zone", "zone-1"); + DecommissionStatus decommissionStatus = randomFrom( + DecommissionStatus.DECOMMISSION_IN_PROGRESS, + DecommissionStatus.DECOMMISSION_SUCCESSFUL + ); + DecommissionAttributeMetadata decommissionAttributeMetadata = new DecommissionAttributeMetadata( + decommissionAttribute, + decommissionStatus + ); + Metadata.Builder metaBuilder = Metadata.builder(); + metaBuilder.putCustom(DecommissionAttributeMetadata.TYPE, decommissionAttributeMetadata); + Metadata metadata = metaBuilder.build(); + + DiscoveryNode discoveryNode = newDiscoveryNode(Collections.singletonMap("zone", "zone-1")); + expectThrows( + NodeDecommissionedException.class, + () -> JoinTaskExecutor.ensureNodeNotDecommissioned(discoveryNode, metadata) + ); + } + + public void testJoinClusterWithDifferentDecommission() { + Settings.builder().build(); + DecommissionAttribute decommissionAttribute = new DecommissionAttribute("zone", "zone-1"); + DecommissionStatus decommissionStatus = randomFrom(DecommissionStatus.values()); + DecommissionAttributeMetadata decommissionAttributeMetadata = new DecommissionAttributeMetadata( + decommissionAttribute, + decommissionStatus + ); + Metadata.Builder metaBuilder = Metadata.builder(); + metaBuilder.putCustom(DecommissionAttributeMetadata.TYPE, decommissionAttributeMetadata); + Metadata metadata = metaBuilder.build(); + + DiscoveryNode discoveryNode = newDiscoveryNode(Collections.singletonMap("zone", "zone-2")); + JoinTaskExecutor.ensureNodeNotDecommissioned(discoveryNode, metadata); + } + + public void testJoinClusterWithDecommissionFailedOrInitOrRecommission() { + Settings.builder().build(); + DecommissionAttribute decommissionAttribute = new DecommissionAttribute("zone", "zone-1"); + DecommissionStatus decommissionStatus = randomFrom( + DecommissionStatus.DECOMMISSION_INIT, + DecommissionStatus.DECOMMISSION_FAILED, + DecommissionStatus.RECOMMISSION_IN_PROGRESS + ); + DecommissionAttributeMetadata decommissionAttributeMetadata = new DecommissionAttributeMetadata( + decommissionAttribute, + decommissionStatus + ); + Metadata.Builder metaBuilder = Metadata.builder(); + metaBuilder.putCustom(DecommissionAttributeMetadata.TYPE, decommissionAttributeMetadata); + Metadata metadata = metaBuilder.build(); + + DiscoveryNode discoveryNode = newDiscoveryNode(Collections.singletonMap("zone", "zone-1")); + JoinTaskExecutor.ensureNodeNotDecommissioned(discoveryNode, metadata); + } + + private DiscoveryNode newDiscoveryNode(Map attributes) { + return new DiscoveryNode( + randomAlphaOfLength(10), + randomAlphaOfLength(10), + buildNewFakeTransportAddress(), + attributes, + Collections.singleton(DiscoveryNodeRole.CLUSTER_MANAGER_ROLE), + Version.CURRENT + ); + } } From a1c3bf10fae652596fc853c691a1cadf0ee27d3d Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Thu, 25 Aug 2022 00:47:20 +0530 Subject: [PATCH 041/187] Fix Signed-off-by: Rishab Nahata --- .../org/opensearch/cluster/coordination/JoinTaskExecutor.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java index 469c0b786056f..7008474222aef 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java @@ -482,8 +482,7 @@ public static void ensureNodeNotDecommissioned(DiscoveryNode node, Metadata meta DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute(); DecommissionStatus status = decommissionAttributeMetadata.status(); if (decommissionAttribute != null && status != null) { - // We will let the node join the cluster if the current status is INIT or FAILED - if(status.equals(DecommissionStatus.DECOMMISSION_FAILED) || status.equals(DecommissionStatus.DECOMMISSION_INIT)) return; + // We will let the node join the cluster if the current status is not IN_PROGRESS or SUCCESSFUL if (node.getAttributes().get(decommissionAttribute.attributeName()).equals(decommissionAttribute.attributeValue()) && ( status.equals(DecommissionStatus.DECOMMISSION_IN_PROGRESS) From 4f941406b5cc8e29010427e2fd8bce9cc43210fe Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Thu, 25 Aug 2022 16:17:26 +0530 Subject: [PATCH 042/187] Test files Signed-off-by: Rishab Nahata --- .../decommission/DecommissionHelperTests.java | 14 ++++++++++++++ .../decommission/DecommissionServiceTests.java | 14 ++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 server/src/test/java/org/opensearch/cluster/decommission/DecommissionHelperTests.java create mode 100644 server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionHelperTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionHelperTests.java new file mode 100644 index 0000000000000..289d5147512e1 --- /dev/null +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionHelperTests.java @@ -0,0 +1,14 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.decommission; + +import org.opensearch.test.OpenSearchTestCase; + +public class DecommissionHelperTests extends OpenSearchTestCase { +} diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java new file mode 100644 index 0000000000000..ea4aee984df98 --- /dev/null +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java @@ -0,0 +1,14 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.decommission; + +import org.opensearch.test.OpenSearchTestCase; + +public class DecommissionServiceTests extends OpenSearchTestCase { +} From 5d705c848911856323757a768a2c1d4bba337010 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Thu, 25 Aug 2022 17:14:49 +0530 Subject: [PATCH 043/187] Move observer logic to helper Signed-off-by: Rishab Nahata --- .../decommission/DecommissionHelper.java | 52 +++++++++++++-- .../decommission/DecommissionService.java | 58 ++++++++--------- .../decommission/DecommissionHelperTests.java | 65 +++++++++++++++++++ 3 files changed, 139 insertions(+), 36 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java index ce7befacaef98..7b2ae71288d71 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java @@ -10,17 +10,24 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.opensearch.action.ActionListener; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.ClusterStateObserver; import org.opensearch.cluster.ClusterStateTaskConfig; import org.opensearch.cluster.ClusterStateTaskListener; +import org.opensearch.cluster.ack.ClusterStateUpdateResponse; import org.opensearch.cluster.coordination.NodeRemovalClusterStateTaskExecutor; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.routing.allocation.AllocationService; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Priority; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.threadpool.ThreadPool; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.function.Predicate; /** * Helper executor class to remove list of nodes from the cluster @@ -34,24 +41,61 @@ public class DecommissionHelper { private final NodeRemovalClusterStateTaskExecutor nodeRemovalExecutor; private final ClusterService clusterService; + private final ThreadPool threadPool; - DecommissionHelper(ClusterService clusterService, AllocationService allocationService) { + DecommissionHelper( + ClusterService clusterService, + AllocationService allocationService, + ThreadPool threadPool + ) { this.clusterService = clusterService; this.nodeRemovalExecutor = new NodeRemovalClusterStateTaskExecutor(allocationService, logger); + this.threadPool = threadPool; } - public void handleNodesDecommissionRequest(List nodesToBeDecommissioned, String reason) { + public void handleNodesDecommissionRequest( + List nodesToBeDecommissioned, + String reason, + TimeValue timeout, + Predicate allDecommissionedNodesRemoved, + ActionListener nodesRemovedListener + ) { final Map nodesDecommissionTasks = new LinkedHashMap<>(); nodesToBeDecommissioned.forEach(discoveryNode -> { final NodeRemovalClusterStateTaskExecutor.Task task = new NodeRemovalClusterStateTaskExecutor.Task(discoveryNode, reason); nodesDecommissionTasks.put(task, nodeRemovalExecutor); }); - final String source = "node-decommissioned"; clusterService.submitStateUpdateTasks( - source, + "node-decommissioned", nodesDecommissionTasks, ClusterStateTaskConfig.build(Priority.IMMEDIATE), nodeRemovalExecutor ); + + final ClusterStateObserver observer = new ClusterStateObserver( + clusterService, + timeout, + logger, + threadPool.getThreadContext() + ); + + observer.waitForNextChange(new ClusterStateObserver.Listener() { + @Override + public void onNewClusterState(ClusterState state) { + logger.info("successfully removed all decommissioned nodes [{}] from the cluster", nodesToBeDecommissioned.toString()); + nodesRemovedListener.onResponse(new ClusterStateUpdateResponse(true)); + } + + @Override + public void onClusterServiceClose() { + logger.debug("cluster service closed while waiting for removal of decommissioned nodes."); + } + + @Override + public void onTimeout(TimeValue timeout) { + logger.info("timed out while waiting for removal of decommissioned nodes"); + nodesRemovedListener.onResponse(new ClusterStateUpdateResponse(false)); + } + }, allDecommissionedNodesRemoved); } } diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 139b241ea00a4..c1e0045000ee7 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -84,7 +84,11 @@ public DecommissionService( this.clusterService = clusterService; this.transportService = transportService; this.threadPool = threadPool; - this.decommissionHelper = new DecommissionHelper(clusterService, allocationService); + this.decommissionHelper = new DecommissionHelper( + clusterService, + allocationService, + threadPool + ); this.awarenessAttributes = AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING.get(settings); clusterSettings.addSettingsUpdateConsumer( AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING, @@ -318,44 +322,34 @@ public void onFailure(Exception e) { } }; - // execute decommissioning - decommissionHelper.handleNodesDecommissionRequest( - nodesWithDecommissionAttribute(state, decommissionAttribute), - "nodes-decommissioned" - ); + ActionListener nodesRemovalListener = new ActionListener<>() { + @Override + public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { + DecommissionStatus updateStatusTo = clusterStateUpdateResponse.isAcknowledged() ? + DecommissionStatus.DECOMMISSION_SUCCESSFUL : DecommissionStatus.DECOMMISSION_FAILED; + updateMetadataWithDecommissionStatus(updateStatusTo, statusUpdateListener); + } - final ClusterStateObserver observer = new ClusterStateObserver( - clusterService, - TimeValue.timeValueSeconds(30L), // should this be a setting? - logger, - threadPool.getThreadContext() - ); + @Override + public void onFailure(Exception e) { + logger.error("failed to update the decommission status"); + } + }; final Predicate allDecommissionedNodesRemoved = clusterState -> { List nodesWithDecommissionAttribute = nodesWithDecommissionAttribute(clusterState, decommissionAttribute); return nodesWithDecommissionAttribute.size() == 0; }; - observer.waitForNextChange(new ClusterStateObserver.Listener() { - @Override - public void onNewClusterState(ClusterState state) { - logger.info("successfully removed all decommissioned nodes from the cluster"); - clearVotingConfigAfterSuccessfulDecommission(); - updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_SUCCESSFUL, statusUpdateListener); - } - - @Override - public void onClusterServiceClose() { - logger.debug("cluster service closed while waiting for removal of decommissioned nodes."); - } - - @Override - public void onTimeout(TimeValue timeout) { - logger.info("timed out while waiting for removal of decommissioned nodes"); - clearVotingConfigAfterSuccessfulDecommission(); - updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_FAILED, statusUpdateListener); - } - }, allDecommissionedNodesRemoved); + // execute decommissioning + decommissionHelper.handleNodesDecommissionRequest( + nodesWithDecommissionAttribute(state, decommissionAttribute), + "nodes-decommissioned", + TimeValue.timeValueSeconds(30L), + allDecommissionedNodesRemoved, + nodesRemovalListener + ); + clearVotingConfigAfterSuccessfulDecommission(); } private void updateMetadataWithDecommissionStatus( diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionHelperTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionHelperTests.java index 289d5147512e1..722fad288e6bb 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionHelperTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionHelperTests.java @@ -8,7 +8,72 @@ package org.opensearch.cluster.decommission; +import org.opensearch.Version; +import org.opensearch.cluster.ClusterName; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.node.DiscoveryNodeRole; +import org.opensearch.cluster.node.DiscoveryNodes; +import org.opensearch.cluster.routing.allocation.AllocationService; +import org.opensearch.cluster.service.ClusterService; import org.opensearch.test.OpenSearchTestCase; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static java.util.Collections.singletonMap; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + public class DecommissionHelperTests extends OpenSearchTestCase { + +// public void testRemoveNodesForDecommissionRequest() { +// final AllocationService allocationService = mock(AllocationService.class); +// final ClusterService clusterService = mock(ClusterService.class); +// +// ClusterState clusterState = ClusterState.builder(new ClusterName("test")).build(); +// +// logger.info("--> adding five nodes on same zone_1"); +// clusterState = addNodes(clusterState, "zone_1", "node1", "node2", "node3", "node4", "node5"); +// +// logger.info("--> adding five nodes on same zone_2"); +// clusterState = addNodes(clusterState, "zone_2", "node6", "node7", "node8", "node9", "node10"); +// +// logger.info("--> adding five nodes on same zone_3"); +// clusterState = addNodes(clusterState, "zone_3", "node11", "node12", "node13", "node14", "node15"); +// +// when(clusterService.state()).thenReturn(clusterState); +// +// final DecommissionHelper decommissionHelper = new DecommissionHelper(clusterService, allocationService); +// +// List nodesToBeRemoved = new ArrayList<>(); +// nodesToBeRemoved.add(clusterState.nodes().get("node11")); +// nodesToBeRemoved.add(clusterState.nodes().get("node12")); +// nodesToBeRemoved.add(clusterState.nodes().get("node13")); +// nodesToBeRemoved.add(clusterState.nodes().get("node14")); +// nodesToBeRemoved.add(clusterState.nodes().get("node15")); +// +// decommissionHelper.handleNodesDecommissionRequest(nodesToBeRemoved, "unit-test"); +// assertEquals((clusterService.state().nodes().getSize()), 10); +// } + + private ClusterState addNodes(ClusterState clusterState, String zone, String... nodeIds) { + DiscoveryNodes.Builder nodeBuilder = DiscoveryNodes.builder(clusterState.nodes()); + org.opensearch.common.collect.List.of(nodeIds).forEach(nodeId -> nodeBuilder.add(newNode(nodeId, singletonMap("zone", zone)))); + clusterState = ClusterState.builder(clusterState).nodes(nodeBuilder).build(); + return clusterState; + } + + private DiscoveryNode newNode(String nodeId, Map attributes) { + return new DiscoveryNode(nodeId, buildNewFakeTransportAddress(), attributes, CLUSTER_MANAGER_DATA_ROLES, Version.CURRENT); + } + + final private static Set CLUSTER_MANAGER_DATA_ROLES = Collections.unmodifiableSet( + new HashSet<>(Arrays.asList(DiscoveryNodeRole.CLUSTER_MANAGER_ROLE, DiscoveryNodeRole.DATA_ROLE)) + ); } From e30d29bff9962236c5465a1e4d90260820bb5276 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Thu, 25 Aug 2022 17:23:56 +0530 Subject: [PATCH 044/187] fix msg Signed-off-by: Rishab Nahata --- .../opensearch/cluster/decommission/DecommissionService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index c1e0045000ee7..b034daa567361 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -332,7 +332,7 @@ public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { @Override public void onFailure(Exception e) { - logger.error("failed to update the decommission status"); + logger.error("error while waiting for decommissioned nodes to be removed", e); } }; @@ -341,7 +341,7 @@ public void onFailure(Exception e) { return nodesWithDecommissionAttribute.size() == 0; }; - // execute decommissioning + // execute nodes decommissioning and wait for it to complete decommissionHelper.handleNodesDecommissionRequest( nodesWithDecommissionAttribute(state, decommissionAttribute), "nodes-decommissioned", From 55957531ae4636c69c109f61bc4649e604844865 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Fri, 26 Aug 2022 14:25:47 +0530 Subject: [PATCH 045/187] Move predicate to helper Signed-off-by: Rishab Nahata --- .../decommission/DecommissionHelper.java | 20 ++++++++++--- .../decommission/DecommissionService.java | 12 +++----- .../decommission/DecommissionHelperTests.java | 29 ++++++++++++++++++- 3 files changed, 48 insertions(+), 13 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java index 7b2ae71288d71..935fe42873bc9 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java @@ -24,9 +24,10 @@ import org.opensearch.common.unit.TimeValue; import org.opensearch.threadpool.ThreadPool; +import java.util.Iterator; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; +import java.util.Set; import java.util.function.Predicate; /** @@ -54,10 +55,9 @@ public class DecommissionHelper { } public void handleNodesDecommissionRequest( - List nodesToBeDecommissioned, + Set nodesToBeDecommissioned, String reason, TimeValue timeout, - Predicate allDecommissionedNodesRemoved, ActionListener nodesRemovedListener ) { final Map nodesDecommissionTasks = new LinkedHashMap<>(); @@ -72,6 +72,18 @@ public void handleNodesDecommissionRequest( nodeRemovalExecutor ); + Predicate allDecommissionedNodesRemovedPredicate = clusterState -> { + Iterator nodesIter = clusterState.nodes().getNodes().valuesIt(); + while (nodesIter.hasNext()) { + final DiscoveryNode node = nodesIter.next(); + // check if the node is part of node decommissioned list + if (nodesToBeDecommissioned.contains(node)) { + return false; + } + } + return true; + }; + final ClusterStateObserver observer = new ClusterStateObserver( clusterService, timeout, @@ -96,6 +108,6 @@ public void onTimeout(TimeValue timeout) { logger.info("timed out while waiting for removal of decommissioned nodes"); nodesRemovedListener.onResponse(new ClusterStateUpdateResponse(false)); } - }, allDecommissionedNodesRemoved); + }, allDecommissionedNodesRemovedPredicate); } } diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index b034daa567361..b0e2e2f1f7a46 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -42,8 +42,10 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Set; import java.util.function.Predicate; /** @@ -336,17 +338,11 @@ public void onFailure(Exception e) { } }; - final Predicate allDecommissionedNodesRemoved = clusterState -> { - List nodesWithDecommissionAttribute = nodesWithDecommissionAttribute(clusterState, decommissionAttribute); - return nodesWithDecommissionAttribute.size() == 0; - }; - // execute nodes decommissioning and wait for it to complete decommissionHelper.handleNodesDecommissionRequest( nodesWithDecommissionAttribute(state, decommissionAttribute), "nodes-decommissioned", TimeValue.timeValueSeconds(30L), - allDecommissionedNodesRemoved, nodesRemovalListener ); clearVotingConfigAfterSuccessfulDecommission(); @@ -383,8 +379,8 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS }); } - private List nodesWithDecommissionAttribute(ClusterState clusterState, DecommissionAttribute decommissionAttribute) { - List nodesWithDecommissionAttribute = new ArrayList<>(); + private Set nodesWithDecommissionAttribute(ClusterState clusterState, DecommissionAttribute decommissionAttribute) { + Set nodesWithDecommissionAttribute = new HashSet<>(); final Predicate shouldRemoveNodePredicate = discoveryNode -> nodeHasDecommissionedAttribute( discoveryNode, decommissionAttribute diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionHelperTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionHelperTests.java index 722fad288e6bb..9452c0e94c262 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionHelperTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionHelperTests.java @@ -8,15 +8,24 @@ package org.opensearch.cluster.decommission; +import org.junit.AfterClass; +import org.junit.BeforeClass; import org.opensearch.Version; import org.opensearch.cluster.ClusterName; import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.ClusterStateObserver; +import org.opensearch.cluster.coordination.CoordinationMetadata; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.node.DiscoveryNodeRole; import org.opensearch.cluster.node.DiscoveryNodes; import org.opensearch.cluster.routing.allocation.AllocationService; import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.settings.ClusterSettings; +import org.opensearch.common.settings.Settings; import org.opensearch.test.OpenSearchTestCase; +import org.opensearch.threadpool.TestThreadPool; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportService; import java.util.ArrayList; import java.util.Arrays; @@ -26,12 +35,30 @@ import java.util.Map; import java.util.Set; +import static java.util.Collections.emptyMap; +import static java.util.Collections.emptySet; import static java.util.Collections.singletonMap; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static org.opensearch.test.ClusterServiceUtils.createClusterService; public class DecommissionHelperTests extends OpenSearchTestCase { + private static ThreadPool threadPool; + private static ClusterService clusterService; + + @BeforeClass + public static void createThreadPoolAndClusterService() { + threadPool = new TestThreadPool("test", Settings.EMPTY); + clusterService = createClusterService(threadPool); + } + + @AfterClass + public static void shutdownThreadPoolAndClusterService() { + clusterService.stop(); + threadPool.shutdown(); + } + // public void testRemoveNodesForDecommissionRequest() { // final AllocationService allocationService = mock(AllocationService.class); // final ClusterService clusterService = mock(ClusterService.class); @@ -69,7 +96,7 @@ private ClusterState addNodes(ClusterState clusterState, String zone, String... return clusterState; } - private DiscoveryNode newNode(String nodeId, Map attributes) { + private static DiscoveryNode newNode(String nodeId, Map attributes) { return new DiscoveryNode(nodeId, buildNewFakeTransportAddress(), attributes, CLUSTER_MANAGER_DATA_ROLES, Version.CURRENT); } From 9f73e82af91dedf28afa1cec0ab195a806dc889a Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Mon, 29 Aug 2022 14:45:40 +0530 Subject: [PATCH 046/187] test Signed-off-by: Rishab Nahata --- ...elper.java => DecommissionController.java} | 51 ++++- .../decommission/DecommissionService.java | 50 +---- .../DecommissionControllerTests.java | 179 ++++++++++++++++++ .../decommission/DecommissionHelperTests.java | 106 ----------- 4 files changed, 232 insertions(+), 154 deletions(-) rename server/src/main/java/org/opensearch/cluster/decommission/{DecommissionHelper.java => DecommissionController.java} (61%) create mode 100644 server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java delete mode 100644 server/src/test/java/org/opensearch/cluster/decommission/DecommissionHelperTests.java diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java similarity index 61% rename from server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java rename to server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java index 935fe42873bc9..87fe15f35be38 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java @@ -10,13 +10,17 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.message.ParameterizedMessage; import org.opensearch.action.ActionListener; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateObserver; import org.opensearch.cluster.ClusterStateTaskConfig; import org.opensearch.cluster.ClusterStateTaskListener; +import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; import org.opensearch.cluster.coordination.NodeRemovalClusterStateTaskExecutor; +import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; +import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.routing.allocation.AllocationService; import org.opensearch.cluster.service.ClusterService; @@ -36,15 +40,15 @@ * @opensearch.internal */ -public class DecommissionHelper { +public class DecommissionController { - private static final Logger logger = LogManager.getLogger(DecommissionHelper.class); + private static final Logger logger = LogManager.getLogger(DecommissionController.class); private final NodeRemovalClusterStateTaskExecutor nodeRemovalExecutor; private final ClusterService clusterService; private final ThreadPool threadPool; - DecommissionHelper( + DecommissionController( ClusterService clusterService, AllocationService allocationService, ThreadPool threadPool @@ -110,4 +114,45 @@ public void onTimeout(TimeValue timeout) { } }, allDecommissionedNodesRemovedPredicate); } + + public void updateMetadataWithDecommissionStatus( + DecommissionStatus decommissionStatus, + ActionListener listener + ) { + clusterService.submitStateUpdateTask(decommissionStatus.status(), new ClusterStateUpdateTask(Priority.URGENT) { + @Override + public ClusterState execute(ClusterState currentState) throws Exception { + Metadata metadata = currentState.metadata(); + DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.custom(DecommissionAttributeMetadata.TYPE); + assert decommissionAttributeMetadata != null && decommissionAttributeMetadata.decommissionAttribute() != null + : "failed to update status for decommission. metadata doesn't exist or invalid"; + assert assertIncrementalStatusOrFailed(decommissionAttributeMetadata.status(), decommissionStatus); + Metadata.Builder mdBuilder = Metadata.builder(metadata); + DecommissionAttributeMetadata newMetadata = decommissionAttributeMetadata.withUpdatedStatus(decommissionStatus); + mdBuilder.putCustom(DecommissionAttributeMetadata.TYPE, newMetadata); + return ClusterState.builder(currentState).metadata(mdBuilder).build(); + } + + @Override + public void onFailure(String source, Exception e) { + logger.error(() -> new ParameterizedMessage("failed to mark status as [{}]", decommissionStatus.status()), e); + } + + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + listener.onResponse(new ClusterStateUpdateResponse(true)); + } + + }); + } + + private static boolean assertIncrementalStatusOrFailed(DecommissionStatus oldStatus, DecommissionStatus newStatus) { + if (oldStatus == null || newStatus.equals(DecommissionStatus.DECOMMISSION_FAILED)) return true; + else if (newStatus.equals(DecommissionStatus.DECOMMISSION_SUCCESSFUL)) { + return oldStatus.equals(DecommissionStatus.DECOMMISSION_IN_PROGRESS); + } else if (newStatus.equals(DecommissionStatus.DECOMMISSION_IN_PROGRESS)) { + return oldStatus.equals(DecommissionStatus.DECOMMISSION_INIT); + } + return true; + } } diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index b0e2e2f1f7a46..971da583eabe5 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -19,7 +19,6 @@ import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsRequest; import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsResponse; import org.opensearch.cluster.ClusterState; -import org.opensearch.cluster.ClusterStateObserver; import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.NotClusterManagerException; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; @@ -71,7 +70,7 @@ public class DecommissionService { private final ClusterService clusterService; private final TransportService transportService; private final ThreadPool threadPool; - private final DecommissionHelper decommissionHelper; + private final DecommissionController decommissionController; private volatile List awarenessAttributes; @Inject @@ -86,7 +85,7 @@ public DecommissionService( this.clusterService = clusterService; this.transportService = transportService; this.threadPool = threadPool; - this.decommissionHelper = new DecommissionHelper( + this.decommissionController = new DecommissionController( clusterService, allocationService, threadPool @@ -302,7 +301,7 @@ public void onFailure(Exception e) { ); } }; - updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_IN_PROGRESS, listener); + decommissionController.updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_IN_PROGRESS, listener); // TODO - code for graceful decommission } @@ -329,7 +328,7 @@ public void onFailure(Exception e) { public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { DecommissionStatus updateStatusTo = clusterStateUpdateResponse.isAcknowledged() ? DecommissionStatus.DECOMMISSION_SUCCESSFUL : DecommissionStatus.DECOMMISSION_FAILED; - updateMetadataWithDecommissionStatus(updateStatusTo, statusUpdateListener); + decommissionController.updateMetadataWithDecommissionStatus(updateStatusTo, statusUpdateListener); } @Override @@ -339,7 +338,7 @@ public void onFailure(Exception e) { }; // execute nodes decommissioning and wait for it to complete - decommissionHelper.handleNodesDecommissionRequest( + decommissionController.handleNodesDecommissionRequest( nodesWithDecommissionAttribute(state, decommissionAttribute), "nodes-decommissioned", TimeValue.timeValueSeconds(30L), @@ -348,36 +347,7 @@ public void onFailure(Exception e) { clearVotingConfigAfterSuccessfulDecommission(); } - private void updateMetadataWithDecommissionStatus( - DecommissionStatus decommissionStatus, - ActionListener listener - ) { - clusterService.submitStateUpdateTask(decommissionStatus.status(), new ClusterStateUpdateTask(Priority.URGENT) { - @Override - public ClusterState execute(ClusterState currentState) throws Exception { - Metadata metadata = currentState.metadata(); - DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.custom(DecommissionAttributeMetadata.TYPE); - assert decommissionAttributeMetadata != null && decommissionAttributeMetadata.decommissionAttribute() != null - : "failed to update status for decommission. metadata doesn't exist or invalid"; - assert assertIncrementalStatusOrFailed(decommissionAttributeMetadata.status(), decommissionStatus); - Metadata.Builder mdBuilder = Metadata.builder(metadata); - DecommissionAttributeMetadata newMetadata = decommissionAttributeMetadata.withUpdatedStatus(decommissionStatus); - mdBuilder.putCustom(DecommissionAttributeMetadata.TYPE, newMetadata); - return ClusterState.builder(currentState).metadata(mdBuilder).build(); - } - @Override - public void onFailure(String source, Exception e) { - logger.error(() -> new ParameterizedMessage("failed to mark status as [{}]", decommissionStatus.status()), e); - } - - @Override - public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { - listener.onResponse(new ClusterStateUpdateResponse(true)); - } - - }); - } private Set nodesWithDecommissionAttribute(ClusterState clusterState, DecommissionAttribute decommissionAttribute) { Set nodesWithDecommissionAttribute = new HashSet<>(); @@ -395,16 +365,6 @@ private Set nodesWithDecommissionAttribute(ClusterState clusterSt return nodesWithDecommissionAttribute; } - private static boolean assertIncrementalStatusOrFailed(DecommissionStatus oldStatus, DecommissionStatus newStatus) { - if (oldStatus == null || newStatus.equals(DecommissionStatus.DECOMMISSION_FAILED)) return true; - else if (newStatus.equals(DecommissionStatus.DECOMMISSION_SUCCESSFUL)) { - return oldStatus.equals(DecommissionStatus.DECOMMISSION_IN_PROGRESS); - } else if (newStatus.equals(DecommissionStatus.DECOMMISSION_IN_PROGRESS)) { - return oldStatus.equals(DecommissionStatus.DECOMMISSION_INIT); - } - return true; - } - private static boolean nodeHasDecommissionedAttribute(DiscoveryNode discoveryNode, DecommissionAttribute decommissionAttribute) { return discoveryNode.getAttributes().get(decommissionAttribute.attributeName()).equals(decommissionAttribute.attributeValue()); } diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java new file mode 100644 index 0000000000000..33861b09c2596 --- /dev/null +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java @@ -0,0 +1,179 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.decommission; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.opensearch.Version; +import org.opensearch.action.ActionListener; +import org.opensearch.action.admin.cluster.configuration.TransportAddVotingConfigExclusionsActionTests; +import org.opensearch.cluster.ClusterName; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.ClusterStateObserver; +import org.opensearch.cluster.ClusterStateUpdateTask; +import org.opensearch.cluster.ack.ClusterStateUpdateResponse; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.node.DiscoveryNodeRole; +import org.opensearch.cluster.node.DiscoveryNodes; +import org.opensearch.cluster.routing.allocation.AllocationService; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.test.OpenSearchTestCase; +import org.opensearch.threadpool.TestThreadPool; +import org.opensearch.threadpool.ThreadPool; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static java.util.Collections.singletonMap; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.sameInstance; +import static org.mockito.Mockito.mock; +import static org.opensearch.cluster.ClusterState.builder; +import static org.opensearch.test.ClusterServiceUtils.createClusterService; +import static org.opensearch.test.ClusterServiceUtils.setState; + +public class DecommissionControllerTests extends OpenSearchTestCase { + + private static ThreadPool threadPool; + private static ClusterService clusterService; + private DecommissionController decommissionController; + private ClusterStateObserver clusterStateObserver; + + @BeforeClass + public static void createThreadPoolAndClusterService() { + threadPool = new TestThreadPool("test", Settings.EMPTY); + clusterService = createClusterService(threadPool); + } + + @AfterClass + public static void shutdownThreadPoolAndClusterService() { + clusterService.stop(); + threadPool.shutdown(); + } + + @Before + public void setupForTests() { + ClusterState clusterState = ClusterState.builder(new ClusterName("test")).build(); + logger.info("--> adding five nodes on same zone_1"); + clusterState = addNodes(clusterState, "zone_1", "node1", "node2", "node3", "node4", "node5"); + logger.info("--> adding five nodes on same zone_2"); + clusterState = addNodes(clusterState, "zone_2", "node6", "node7", "node8", "node9", "node10"); + logger.info("--> adding five nodes on same zone_3"); + clusterState = addNodes(clusterState, "zone_3", "node11", "node12", "node13", "node14", "node15"); + + final ClusterState.Builder builder = builder(clusterState); + setState(clusterService, builder); + + final AllocationService allocationService = mock(AllocationService.class); + decommissionController = new DecommissionController(clusterService, allocationService, threadPool); + clusterStateObserver = new ClusterStateObserver(clusterService, null, logger, threadPool.getThreadContext()); + } + + public void testRemoveNodesForDecommissionRequest() throws InterruptedException{ + final CountDownLatch countDownLatch = new CountDownLatch(2); + + Set nodesToBeRemoved = new HashSet<>(); + nodesToBeRemoved.add(clusterService.state().nodes().get("node11")); + nodesToBeRemoved.add(clusterService.state().nodes().get("node12")); + nodesToBeRemoved.add(clusterService.state().nodes().get("node13")); + nodesToBeRemoved.add(clusterService.state().nodes().get("node14")); + nodesToBeRemoved.add(clusterService.state().nodes().get("node15")); + + ActionListener actionListener = new ActionListener() { + @Override + public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { + logger.info("test"); + } + + @Override + public void onFailure(Exception e) { + } + }; + + clusterStateObserver.waitForNextChange(new UpdateClusterStateForDecommission(countDownLatch, nodesToBeRemoved)); + decommissionController.handleNodesDecommissionRequest( + nodesToBeRemoved, + "unit-test", + TimeValue.timeValueSeconds(30L), + actionListener + ); + + assertTrue(countDownLatch.await(30, TimeUnit.SECONDS)); + assertEquals(clusterService.getClusterApplierService().state().nodes().getDataNodes().size(), 10); + } + + private ClusterState addNodes(ClusterState clusterState, String zone, String... nodeIds) { + DiscoveryNodes.Builder nodeBuilder = DiscoveryNodes.builder(clusterState.nodes()); + org.opensearch.common.collect.List.of(nodeIds).forEach(nodeId -> nodeBuilder.add(newNode(nodeId, singletonMap("zone", zone)))); + clusterState = ClusterState.builder(clusterState).nodes(nodeBuilder).build(); + return clusterState; + } + + private static DiscoveryNode newNode(String nodeId, Map attributes) { + return new DiscoveryNode(nodeId, buildNewFakeTransportAddress(), attributes, CLUSTER_MANAGER_DATA_ROLES, Version.CURRENT); + } + + final private static Set CLUSTER_MANAGER_DATA_ROLES = Collections.unmodifiableSet( + new HashSet<>(Arrays.asList(DiscoveryNodeRole.CLUSTER_MANAGER_ROLE, DiscoveryNodeRole.DATA_ROLE)) + ); + + private static class UpdateClusterStateForDecommission implements ClusterStateObserver.Listener { + + final CountDownLatch doneLatch; + final Set discoveryNodes; + + UpdateClusterStateForDecommission(CountDownLatch latch, Set discoveryNodes) { + this.doneLatch = latch; + this.discoveryNodes = discoveryNodes; + } + + @Override + public void onNewClusterState(ClusterState state) { + clusterService.getClusterManagerService().submitStateUpdateTask("decommission", new ClusterStateUpdateTask() { + @Override + public ClusterState execute(ClusterState currentState) throws Exception { + assertThat(currentState, sameInstance(state)); + final DiscoveryNodes.Builder remainingNodesBuilder = DiscoveryNodes.builder(currentState.nodes()); + for (DiscoveryNode nodeToBeRemoved : discoveryNodes) { + remainingNodesBuilder.remove(nodeToBeRemoved); + } + return ClusterState.builder(currentState).nodes(remainingNodesBuilder).build(); + } + + @Override + public void onFailure(String source, Exception e) { + throw new AssertionError("unexpected failure", e); + } + + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + doneLatch.countDown(); + } + }); + } + + @Override + public void onClusterServiceClose() { + throw new AssertionError("unexpected close"); + } + + @Override + public void onTimeout(TimeValue timeout) { + throw new AssertionError("unexpected timeout"); + } + } +} diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionHelperTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionHelperTests.java deleted file mode 100644 index 9452c0e94c262..0000000000000 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionHelperTests.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -package org.opensearch.cluster.decommission; - -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.opensearch.Version; -import org.opensearch.cluster.ClusterName; -import org.opensearch.cluster.ClusterState; -import org.opensearch.cluster.ClusterStateObserver; -import org.opensearch.cluster.coordination.CoordinationMetadata; -import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.cluster.node.DiscoveryNodeRole; -import org.opensearch.cluster.node.DiscoveryNodes; -import org.opensearch.cluster.routing.allocation.AllocationService; -import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.settings.ClusterSettings; -import org.opensearch.common.settings.Settings; -import org.opensearch.test.OpenSearchTestCase; -import org.opensearch.threadpool.TestThreadPool; -import org.opensearch.threadpool.ThreadPool; -import org.opensearch.transport.TransportService; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static java.util.Collections.emptyMap; -import static java.util.Collections.emptySet; -import static java.util.Collections.singletonMap; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import static org.opensearch.test.ClusterServiceUtils.createClusterService; - -public class DecommissionHelperTests extends OpenSearchTestCase { - - private static ThreadPool threadPool; - private static ClusterService clusterService; - - @BeforeClass - public static void createThreadPoolAndClusterService() { - threadPool = new TestThreadPool("test", Settings.EMPTY); - clusterService = createClusterService(threadPool); - } - - @AfterClass - public static void shutdownThreadPoolAndClusterService() { - clusterService.stop(); - threadPool.shutdown(); - } - -// public void testRemoveNodesForDecommissionRequest() { -// final AllocationService allocationService = mock(AllocationService.class); -// final ClusterService clusterService = mock(ClusterService.class); -// -// ClusterState clusterState = ClusterState.builder(new ClusterName("test")).build(); -// -// logger.info("--> adding five nodes on same zone_1"); -// clusterState = addNodes(clusterState, "zone_1", "node1", "node2", "node3", "node4", "node5"); -// -// logger.info("--> adding five nodes on same zone_2"); -// clusterState = addNodes(clusterState, "zone_2", "node6", "node7", "node8", "node9", "node10"); -// -// logger.info("--> adding five nodes on same zone_3"); -// clusterState = addNodes(clusterState, "zone_3", "node11", "node12", "node13", "node14", "node15"); -// -// when(clusterService.state()).thenReturn(clusterState); -// -// final DecommissionHelper decommissionHelper = new DecommissionHelper(clusterService, allocationService); -// -// List nodesToBeRemoved = new ArrayList<>(); -// nodesToBeRemoved.add(clusterState.nodes().get("node11")); -// nodesToBeRemoved.add(clusterState.nodes().get("node12")); -// nodesToBeRemoved.add(clusterState.nodes().get("node13")); -// nodesToBeRemoved.add(clusterState.nodes().get("node14")); -// nodesToBeRemoved.add(clusterState.nodes().get("node15")); -// -// decommissionHelper.handleNodesDecommissionRequest(nodesToBeRemoved, "unit-test"); -// assertEquals((clusterService.state().nodes().getSize()), 10); -// } - - private ClusterState addNodes(ClusterState clusterState, String zone, String... nodeIds) { - DiscoveryNodes.Builder nodeBuilder = DiscoveryNodes.builder(clusterState.nodes()); - org.opensearch.common.collect.List.of(nodeIds).forEach(nodeId -> nodeBuilder.add(newNode(nodeId, singletonMap("zone", zone)))); - clusterState = ClusterState.builder(clusterState).nodes(nodeBuilder).build(); - return clusterState; - } - - private static DiscoveryNode newNode(String nodeId, Map attributes) { - return new DiscoveryNode(nodeId, buildNewFakeTransportAddress(), attributes, CLUSTER_MANAGER_DATA_ROLES, Version.CURRENT); - } - - final private static Set CLUSTER_MANAGER_DATA_ROLES = Collections.unmodifiableSet( - new HashSet<>(Arrays.asList(DiscoveryNodeRole.CLUSTER_MANAGER_ROLE, DiscoveryNodeRole.DATA_ROLE)) - ); -} From 520f87bd97962a6b86833bf5a50f1d51d1b7d58a Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Mon, 29 Aug 2022 17:54:59 +0530 Subject: [PATCH 047/187] Add UT Signed-off-by: Rishab Nahata --- .../DecommissionControllerTests.java | 71 ++++++++++++------- 1 file changed, 46 insertions(+), 25 deletions(-) diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java index 33861b09c2596..262186d0dca2c 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java @@ -38,35 +38,34 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import static java.util.Collections.emptyMap; +import static java.util.Collections.emptySet; import static java.util.Collections.singletonMap; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.sameInstance; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import static org.opensearch.cluster.ClusterState.builder; +import static org.opensearch.cluster.OpenSearchAllocationTestCase.createAllocationService; import static org.opensearch.test.ClusterServiceUtils.createClusterService; import static org.opensearch.test.ClusterServiceUtils.setState; public class DecommissionControllerTests extends OpenSearchTestCase { - private static ThreadPool threadPool; - private static ClusterService clusterService; + private ThreadPool threadPool; + private ClusterService clusterService; + private AllocationService allocationService; private DecommissionController decommissionController; private ClusterStateObserver clusterStateObserver; - @BeforeClass - public static void createThreadPoolAndClusterService() { + @Override + public void setUp() throws Exception { + super.setUp(); threadPool = new TestThreadPool("test", Settings.EMPTY); clusterService = createClusterService(threadPool); - } - - @AfterClass - public static void shutdownThreadPoolAndClusterService() { - clusterService.stop(); - threadPool.shutdown(); - } - - @Before - public void setupForTests() { + allocationService = createAllocationService(); ClusterState clusterState = ClusterState.builder(new ClusterName("test")).build(); logger.info("--> adding five nodes on same zone_1"); clusterState = addNodes(clusterState, "zone_1", "node1", "node2", "node3", "node4", "node5"); @@ -74,17 +73,15 @@ public void setupForTests() { clusterState = addNodes(clusterState, "zone_2", "node6", "node7", "node8", "node9", "node10"); logger.info("--> adding five nodes on same zone_3"); clusterState = addNodes(clusterState, "zone_3", "node11", "node12", "node13", "node14", "node15"); - + clusterState = setLocalNodeAsClusterManagerNode(clusterState, "node1"); final ClusterState.Builder builder = builder(clusterState); setState(clusterService, builder); - - final AllocationService allocationService = mock(AllocationService.class); decommissionController = new DecommissionController(clusterService, allocationService, threadPool); clusterStateObserver = new ClusterStateObserver(clusterService, null, logger, threadPool.getThreadContext()); } public void testRemoveNodesForDecommissionRequest() throws InterruptedException{ - final CountDownLatch countDownLatch = new CountDownLatch(2); + final CountDownLatch countDownLatch = new CountDownLatch(1); Set nodesToBeRemoved = new HashSet<>(); nodesToBeRemoved.add(clusterService.state().nodes().get("node11")); @@ -96,6 +93,7 @@ public void testRemoveNodesForDecommissionRequest() throws InterruptedException{ ActionListener actionListener = new ActionListener() { @Override public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { + countDownLatch.countDown(); logger.info("test"); } @@ -103,16 +101,16 @@ public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { public void onFailure(Exception e) { } }; - - clusterStateObserver.waitForNextChange(new UpdateClusterStateForDecommission(countDownLatch, nodesToBeRemoved)); decommissionController.handleNodesDecommissionRequest( nodesToBeRemoved, "unit-test", - TimeValue.timeValueSeconds(30L), + TimeValue.timeValueSeconds(29L), actionListener ); + clusterStateObserver.waitForNextChange(new UpdateClusterStateForDecommission(countDownLatch, nodesToBeRemoved)); assertTrue(countDownLatch.await(30, TimeUnit.SECONDS)); + ClusterState state = clusterService.getClusterApplierService().state(); assertEquals(clusterService.getClusterApplierService().state().nodes().getDataNodes().size(), 10); } @@ -123,15 +121,38 @@ private ClusterState addNodes(ClusterState clusterState, String zone, String... return clusterState; } + private ClusterState addClusterManagerNode(ClusterState clusterState, String zone, String nodeId) { + DiscoveryNodes.Builder nodeBuilder = DiscoveryNodes.builder(clusterState.nodes()); + nodeBuilder.add(newClusterManagerNode(nodeId, singletonMap("zone", zone))); + clusterState = ClusterState.builder(clusterState).nodes(nodeBuilder).build(); + return clusterState; + } + + private ClusterState setLocalNodeAsClusterManagerNode(ClusterState clusterState, String nodeId) { + DiscoveryNodes.Builder nodeBuilder = DiscoveryNodes.builder(clusterState.nodes()); + nodeBuilder.localNodeId(nodeId); + nodeBuilder.clusterManagerNodeId(nodeId); + clusterState = ClusterState.builder(clusterState).nodes(nodeBuilder).build(); + return clusterState; + } + private static DiscoveryNode newNode(String nodeId, Map attributes) { - return new DiscoveryNode(nodeId, buildNewFakeTransportAddress(), attributes, CLUSTER_MANAGER_DATA_ROLES, Version.CURRENT); + return new DiscoveryNode(nodeId, buildNewFakeTransportAddress(), attributes, DATA_ROLE, Version.CURRENT); + } + + private static DiscoveryNode newClusterManagerNode(String nodeId, Map attributes) { + return new DiscoveryNode(nodeId, buildNewFakeTransportAddress(), attributes, CLUSTER_MANAGER_ROLE, Version.CURRENT); } - final private static Set CLUSTER_MANAGER_DATA_ROLES = Collections.unmodifiableSet( - new HashSet<>(Arrays.asList(DiscoveryNodeRole.CLUSTER_MANAGER_ROLE, DiscoveryNodeRole.DATA_ROLE)) + final private static Set CLUSTER_MANAGER_ROLE = Collections.unmodifiableSet( + new HashSet<>(Arrays.asList(DiscoveryNodeRole.CLUSTER_MANAGER_ROLE)) + ); + + final private static Set DATA_ROLE = Collections.unmodifiableSet( + new HashSet<>(Arrays.asList(DiscoveryNodeRole.DATA_ROLE)) ); - private static class UpdateClusterStateForDecommission implements ClusterStateObserver.Listener { + private class UpdateClusterStateForDecommission implements ClusterStateObserver.Listener { final CountDownLatch doneLatch; final Set discoveryNodes; From 5189be32ccf096c429ef531904e09d1c0322f077 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Mon, 29 Aug 2022 21:46:05 +0530 Subject: [PATCH 048/187] Add UT for DecommissionController Signed-off-by: Rishab Nahata --- .../decommission/DecommissionController.java | 28 ++- .../decommission/DecommissionService.java | 48 +++-- .../DecommissionControllerTests.java | 193 ++++++++++-------- 3 files changed, 153 insertions(+), 116 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java index 87fe15f35be38..003fe329da2da 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java @@ -11,6 +11,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; +import org.opensearch.OpenSearchTimeoutException; import org.opensearch.action.ActionListener; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateObserver; @@ -62,7 +63,7 @@ public void handleNodesDecommissionRequest( Set nodesToBeDecommissioned, String reason, TimeValue timeout, - ActionListener nodesRemovedListener + ActionListener nodesRemovedListener ) { final Map nodesDecommissionTasks = new LinkedHashMap<>(); nodesToBeDecommissioned.forEach(discoveryNode -> { @@ -99,7 +100,7 @@ public void handleNodesDecommissionRequest( @Override public void onNewClusterState(ClusterState state) { logger.info("successfully removed all decommissioned nodes [{}] from the cluster", nodesToBeDecommissioned.toString()); - nodesRemovedListener.onResponse(new ClusterStateUpdateResponse(true)); + nodesRemovedListener.onResponse(null); } @Override @@ -110,22 +111,26 @@ public void onClusterServiceClose() { @Override public void onTimeout(TimeValue timeout) { logger.info("timed out while waiting for removal of decommissioned nodes"); - nodesRemovedListener.onResponse(new ClusterStateUpdateResponse(false)); + nodesRemovedListener.onFailure( + new OpenSearchTimeoutException( + "timed out waiting for removal of decommissioned nodes [{}] to take effect", + nodesToBeDecommissioned.toString() + ) + ); } }, allDecommissionedNodesRemovedPredicate); } public void updateMetadataWithDecommissionStatus( DecommissionStatus decommissionStatus, - ActionListener listener + ActionListener listener ) { clusterService.submitStateUpdateTask(decommissionStatus.status(), new ClusterStateUpdateTask(Priority.URGENT) { @Override - public ClusterState execute(ClusterState currentState) throws Exception { + public ClusterState execute(ClusterState currentState) { Metadata metadata = currentState.metadata(); DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.custom(DecommissionAttributeMetadata.TYPE); - assert decommissionAttributeMetadata != null && decommissionAttributeMetadata.decommissionAttribute() != null - : "failed to update status for decommission. metadata doesn't exist or invalid"; + assert decommissionAttributeMetadata != null && decommissionAttributeMetadata.decommissionAttribute() != null; assert assertIncrementalStatusOrFailed(decommissionAttributeMetadata.status(), decommissionStatus); Metadata.Builder mdBuilder = Metadata.builder(metadata); DecommissionAttributeMetadata newMetadata = decommissionAttributeMetadata.withUpdatedStatus(decommissionStatus); @@ -135,19 +140,20 @@ public ClusterState execute(ClusterState currentState) throws Exception { @Override public void onFailure(String source, Exception e) { - logger.error(() -> new ParameterizedMessage("failed to mark status as [{}]", decommissionStatus.status()), e); + listener.onFailure(e); } @Override public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { - listener.onResponse(new ClusterStateUpdateResponse(true)); + DecommissionAttributeMetadata decommissionAttributeMetadata = newState.metadata().custom(DecommissionAttributeMetadata.TYPE); + assert decommissionAttributeMetadata.status().equals(decommissionStatus); + listener.onResponse(null); } - }); } private static boolean assertIncrementalStatusOrFailed(DecommissionStatus oldStatus, DecommissionStatus newStatus) { - if (oldStatus == null || newStatus.equals(DecommissionStatus.DECOMMISSION_FAILED)) return true; + if (newStatus.equals(DecommissionStatus.DECOMMISSION_FAILED)) return true; else if (newStatus.equals(DecommissionStatus.DECOMMISSION_SUCCESSFUL)) { return oldStatus.equals(DecommissionStatus.DECOMMISSION_IN_PROGRESS); } else if (newStatus.equals(DecommissionStatus.DECOMMISSION_IN_PROGRESS)) { diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 971da583eabe5..d935bc02b71d4 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -280,14 +280,14 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS private void initiateGracefulDecommission() { // maybe create a supplier for status update listener? - ActionListener listener = new ActionListener<>() { + ActionListener listener = new ActionListener<>() { @Override - public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { + public void onResponse(Void unused) { logger.info( "updated decommission status to [{}], weighing away awareness attribute for graceful shutdown", DecommissionStatus.DECOMMISSION_IN_PROGRESS ); - failDecommissionedNodes(clusterService.state()); + failDecommissionedNodes(clusterService.getClusterApplierService().state()); } @Override @@ -311,10 +311,13 @@ private void failDecommissionedNodes(ClusterState state) { : "unexpected status encountered while decommissioning nodes"; DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute(); - ActionListener statusUpdateListener = new ActionListener<>() { + ActionListener statusUpdateListener = new ActionListener<>() { @Override - public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { - logger.info("successfully updated decommission status"); + public void onResponse(Void unused) { + logger.info( + "updated decommission status to [{}], decommissioning completed.", + DecommissionStatus.DECOMMISSION_SUCCESSFUL + ); } @Override @@ -323,32 +326,33 @@ public void onFailure(Exception e) { } }; - ActionListener nodesRemovalListener = new ActionListener<>() { - @Override - public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { - DecommissionStatus updateStatusTo = clusterStateUpdateResponse.isAcknowledged() ? - DecommissionStatus.DECOMMISSION_SUCCESSFUL : DecommissionStatus.DECOMMISSION_FAILED; - decommissionController.updateMetadataWithDecommissionStatus(updateStatusTo, statusUpdateListener); - } - - @Override - public void onFailure(Exception e) { - logger.error("error while waiting for decommissioned nodes to be removed", e); - } - }; - // execute nodes decommissioning and wait for it to complete decommissionController.handleNodesDecommissionRequest( nodesWithDecommissionAttribute(state, decommissionAttribute), "nodes-decommissioned", TimeValue.timeValueSeconds(30L), - nodesRemovalListener + new ActionListener() { + @Override + public void onResponse(Void unused) { + decommissionController.updateMetadataWithDecommissionStatus( + DecommissionStatus.DECOMMISSION_SUCCESSFUL, + statusUpdateListener + ); + } + + @Override + public void onFailure(Exception e) { + decommissionController.updateMetadataWithDecommissionStatus( + DecommissionStatus.DECOMMISSION_FAILED, + statusUpdateListener + ); + } + } ); clearVotingConfigAfterSuccessfulDecommission(); } - private Set nodesWithDecommissionAttribute(ClusterState clusterState, DecommissionAttribute decommissionAttribute) { Set nodesWithDecommissionAttribute = new HashSet<>(); final Predicate shouldRemoveNodePredicate = discoveryNode -> nodeHasDecommissionedAttribute( diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java index 262186d0dca2c..809a392d060e2 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java @@ -8,9 +8,11 @@ package org.opensearch.cluster.decommission; +import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; +import org.opensearch.OpenSearchTimeoutException; import org.opensearch.Version; import org.opensearch.action.ActionListener; import org.opensearch.action.admin.cluster.configuration.TransportAddVotingConfigExclusionsActionTests; @@ -19,11 +21,14 @@ import org.opensearch.cluster.ClusterStateObserver; import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; +import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; +import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.node.DiscoveryNodeRole; import org.opensearch.cluster.node.DiscoveryNodes; import org.opensearch.cluster.routing.allocation.AllocationService; import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.collect.ImmutableOpenMap; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; import org.opensearch.test.OpenSearchTestCase; @@ -37,15 +42,21 @@ import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import static java.util.Collections.emptyMap; import static java.util.Collections.emptySet; import static java.util.Collections.singletonMap; import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.sameInstance; +import static org.hamcrest.Matchers.startsWith; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.opensearch.cluster.ClusterState.builder; import static org.opensearch.cluster.OpenSearchAllocationTestCase.createAllocationService; @@ -58,7 +69,6 @@ public class DecommissionControllerTests extends OpenSearchTestCase { private ClusterService clusterService; private AllocationService allocationService; private DecommissionController decommissionController; - private ClusterStateObserver clusterStateObserver; @Override public void setUp() throws Exception { @@ -66,6 +76,10 @@ public void setUp() throws Exception { threadPool = new TestThreadPool("test", Settings.EMPTY); clusterService = createClusterService(threadPool); allocationService = createAllocationService(); + } + + @Before + public void setDefaultClusterState() { ClusterState clusterState = ClusterState.builder(new ClusterName("test")).build(); logger.info("--> adding five nodes on same zone_1"); clusterState = addNodes(clusterState, "zone_1", "node1", "node2", "node3", "node4", "node5"); @@ -75,14 +89,21 @@ public void setUp() throws Exception { clusterState = addNodes(clusterState, "zone_3", "node11", "node12", "node13", "node14", "node15"); clusterState = setLocalNodeAsClusterManagerNode(clusterState, "node1"); final ClusterState.Builder builder = builder(clusterState); - setState(clusterService, builder); + setState( + clusterService, + builder + ); decommissionController = new DecommissionController(clusterService, allocationService, threadPool); - clusterStateObserver = new ClusterStateObserver(clusterService, null, logger, threadPool.getThreadContext()); } - public void testRemoveNodesForDecommissionRequest() throws InterruptedException{ - final CountDownLatch countDownLatch = new CountDownLatch(1); + @After + public void shutdownThreadPoolAndClusterService() { + clusterService.stop(); + threadPool.shutdown(); + } + public void testNodesRemovedForDecommissionRequestSuccessfulResponse() throws InterruptedException { + final CountDownLatch countDownLatch = new CountDownLatch(1); Set nodesToBeRemoved = new HashSet<>(); nodesToBeRemoved.add(clusterService.state().nodes().get("node11")); nodesToBeRemoved.add(clusterService.state().nodes().get("node12")); @@ -90,28 +111,95 @@ public void testRemoveNodesForDecommissionRequest() throws InterruptedException{ nodesToBeRemoved.add(clusterService.state().nodes().get("node14")); nodesToBeRemoved.add(clusterService.state().nodes().get("node15")); - ActionListener actionListener = new ActionListener() { - @Override - public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { - countDownLatch.countDown(); - logger.info("test"); - } + decommissionController.handleNodesDecommissionRequest( + nodesToBeRemoved, + "unit-test", + TimeValue.timeValueSeconds(30L), + new ActionListener() { + @Override + public void onResponse(Void unused) { + countDownLatch.countDown(); + } - @Override - public void onFailure(Exception e) { + @Override + public void onFailure(Exception e) { + fail("there shouldn't have been any failure"); + } } - }; + ); + + assertTrue(countDownLatch.await(30, TimeUnit.SECONDS)); + // test all 5 nodes removed and cluster has 10 nodes + Set nodes = StreamSupport.stream( + clusterService.getClusterApplierService().state().nodes().spliterator(), false + ).collect(Collectors.toSet()); + assertEquals(nodes.size(), 10); + // test no nodes part of zone-3 + for (DiscoveryNode node : nodes) { + assertNotEquals(node.getAttributes().get("zone"), "zone-1"); + } + } + + public void testTimesOut() throws InterruptedException { + final CountDownLatch countDownLatch = new CountDownLatch(1); + Set nodesToBeRemoved = new HashSet<>(); + nodesToBeRemoved.add(clusterService.state().nodes().get("node11")); + nodesToBeRemoved.add(clusterService.state().nodes().get("node12")); + nodesToBeRemoved.add(clusterService.state().nodes().get("node13")); + nodesToBeRemoved.add(clusterService.state().nodes().get("node14")); + nodesToBeRemoved.add(clusterService.state().nodes().get("node15")); decommissionController.handleNodesDecommissionRequest( nodesToBeRemoved, "unit-test", - TimeValue.timeValueSeconds(29L), - actionListener + TimeValue.timeValueMillis(2), + new ActionListener() { + @Override + public void onResponse(Void unused) { + fail("response shouldn't have been called"); + } + + @Override + public void onFailure(Exception e) { + assertThat(e, instanceOf(OpenSearchTimeoutException.class)); + assertThat(e.getMessage(), startsWith("timed out waiting for removal of decommissioned nodes")); + countDownLatch.countDown(); + } + } + ); + assertTrue(countDownLatch.await(30, TimeUnit.SECONDS)); + } + + public void testSuccessfulDecommissionStatusMetadataUpdate() throws InterruptedException { + final CountDownLatch countDownLatch = new CountDownLatch(1); + DecommissionAttributeMetadata oldMetadata = new DecommissionAttributeMetadata( + new DecommissionAttribute("zone", "zone-1"), + DecommissionStatus.DECOMMISSION_IN_PROGRESS ); - clusterStateObserver.waitForNextChange(new UpdateClusterStateForDecommission(countDownLatch, nodesToBeRemoved)); + ClusterState state = clusterService.state(); + Metadata metadata = state.metadata(); + Metadata.Builder mdBuilder = Metadata.builder(metadata); + mdBuilder.putCustom(DecommissionAttributeMetadata.TYPE, oldMetadata); + state = ClusterState.builder(state).metadata(mdBuilder).build(); + setState(clusterService, state); + + decommissionController.updateMetadataWithDecommissionStatus( + DecommissionStatus.DECOMMISSION_SUCCESSFUL, + new ActionListener() { + @Override + public void onResponse(Void unused) { + countDownLatch.countDown(); + } + @Override + public void onFailure(Exception e) { + fail("decommission status update failed"); + } + } + ); assertTrue(countDownLatch.await(30, TimeUnit.SECONDS)); - ClusterState state = clusterService.getClusterApplierService().state(); - assertEquals(clusterService.getClusterApplierService().state().nodes().getDataNodes().size(), 10); + ClusterState newState = clusterService.getClusterApplierService().state(); + DecommissionAttributeMetadata decommissionAttributeMetadata = newState.metadata().custom(DecommissionAttributeMetadata.TYPE); + assertEquals(decommissionAttributeMetadata.status(), DecommissionStatus.DECOMMISSION_SUCCESSFUL); } private ClusterState addNodes(ClusterState clusterState, String zone, String... nodeIds) { @@ -121,13 +209,6 @@ private ClusterState addNodes(ClusterState clusterState, String zone, String... return clusterState; } - private ClusterState addClusterManagerNode(ClusterState clusterState, String zone, String nodeId) { - DiscoveryNodes.Builder nodeBuilder = DiscoveryNodes.builder(clusterState.nodes()); - nodeBuilder.add(newClusterManagerNode(nodeId, singletonMap("zone", zone))); - clusterState = ClusterState.builder(clusterState).nodes(nodeBuilder).build(); - return clusterState; - } - private ClusterState setLocalNodeAsClusterManagerNode(ClusterState clusterState, String nodeId) { DiscoveryNodes.Builder nodeBuilder = DiscoveryNodes.builder(clusterState.nodes()); nodeBuilder.localNodeId(nodeId); @@ -137,64 +218,10 @@ private ClusterState setLocalNodeAsClusterManagerNode(ClusterState clusterState, } private static DiscoveryNode newNode(String nodeId, Map attributes) { - return new DiscoveryNode(nodeId, buildNewFakeTransportAddress(), attributes, DATA_ROLE, Version.CURRENT); + return new DiscoveryNode(nodeId, buildNewFakeTransportAddress(), attributes, CLUSTER_MANAGER_DATA_ROLE, Version.CURRENT); } - private static DiscoveryNode newClusterManagerNode(String nodeId, Map attributes) { - return new DiscoveryNode(nodeId, buildNewFakeTransportAddress(), attributes, CLUSTER_MANAGER_ROLE, Version.CURRENT); - } - - final private static Set CLUSTER_MANAGER_ROLE = Collections.unmodifiableSet( - new HashSet<>(Arrays.asList(DiscoveryNodeRole.CLUSTER_MANAGER_ROLE)) - ); - - final private static Set DATA_ROLE = Collections.unmodifiableSet( - new HashSet<>(Arrays.asList(DiscoveryNodeRole.DATA_ROLE)) + final private static Set CLUSTER_MANAGER_DATA_ROLE = Collections.unmodifiableSet( + new HashSet<>(Arrays.asList(DiscoveryNodeRole.CLUSTER_MANAGER_ROLE, DiscoveryNodeRole.DATA_ROLE)) ); - - private class UpdateClusterStateForDecommission implements ClusterStateObserver.Listener { - - final CountDownLatch doneLatch; - final Set discoveryNodes; - - UpdateClusterStateForDecommission(CountDownLatch latch, Set discoveryNodes) { - this.doneLatch = latch; - this.discoveryNodes = discoveryNodes; - } - - @Override - public void onNewClusterState(ClusterState state) { - clusterService.getClusterManagerService().submitStateUpdateTask("decommission", new ClusterStateUpdateTask() { - @Override - public ClusterState execute(ClusterState currentState) throws Exception { - assertThat(currentState, sameInstance(state)); - final DiscoveryNodes.Builder remainingNodesBuilder = DiscoveryNodes.builder(currentState.nodes()); - for (DiscoveryNode nodeToBeRemoved : discoveryNodes) { - remainingNodesBuilder.remove(nodeToBeRemoved); - } - return ClusterState.builder(currentState).nodes(remainingNodesBuilder).build(); - } - - @Override - public void onFailure(String source, Exception e) { - throw new AssertionError("unexpected failure", e); - } - - @Override - public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { - doneLatch.countDown(); - } - }); - } - - @Override - public void onClusterServiceClose() { - throw new AssertionError("unexpected close"); - } - - @Override - public void onTimeout(TimeValue timeout) { - throw new AssertionError("unexpected timeout"); - } - } } From 611f457d3dab913f05329ef88aeeb3e5acd546f9 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 30 Aug 2022 12:05:10 +0530 Subject: [PATCH 049/187] Improvements and UTs Signed-off-by: Rishab Nahata --- .../AddVotingConfigExclusionsRequest.java | 2 +- .../decommission/DecommissionController.java | 80 +++++++- .../decommission/DecommissionService.java | 186 +++++++++--------- .../DecommissionControllerTests.java | 112 ++++++++++- .../DecommissionServiceTests.java | 172 ++++++++++++++++ 5 files changed, 455 insertions(+), 97 deletions(-) diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/configuration/AddVotingConfigExclusionsRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/configuration/AddVotingConfigExclusionsRequest.java index a2a77a1316898..739bfaf2a3fb1 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/configuration/AddVotingConfigExclusionsRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/configuration/AddVotingConfigExclusionsRequest.java @@ -157,7 +157,7 @@ Set resolveVotingConfigExclusions(ClusterState currentSta } else { assert nodeNames.length >= 1; Map existingNodes = StreamSupport.stream(allNodes.spliterator(), false) - .collect(Collectors.toMap(DiscoveryNode::getName, Function.identity())); + .collect(Collectors.toMap(DiscoveryNode::getName, Function.identity(), (r1, r2) -> r1)); for (String nodeName : nodeNames) { if (existingNodes.containsKey(nodeName)) { diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java index 003fe329da2da..d699fc51e819a 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java @@ -13,6 +13,12 @@ import org.apache.logging.log4j.message.ParameterizedMessage; import org.opensearch.OpenSearchTimeoutException; import org.opensearch.action.ActionListener; +import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsAction; +import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsRequest; +import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsResponse; +import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsAction; +import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsRequest; +import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsResponse; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateObserver; import org.opensearch.cluster.ClusterStateTaskConfig; @@ -26,17 +32,24 @@ import org.opensearch.cluster.routing.allocation.AllocationService; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Priority; +import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.unit.TimeValue; import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.Transport; +import org.opensearch.transport.TransportException; +import org.opensearch.transport.TransportResponseHandler; +import org.opensearch.transport.TransportService; +import java.io.IOException; import java.util.Iterator; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Predicate; /** - * Helper executor class to remove list of nodes from the cluster + * Helper controller class to remove list of nodes from the cluster and update status * * @opensearch.internal */ @@ -47,18 +60,83 @@ public class DecommissionController { private final NodeRemovalClusterStateTaskExecutor nodeRemovalExecutor; private final ClusterService clusterService; + private final TransportService transportService; private final ThreadPool threadPool; DecommissionController( ClusterService clusterService, + TransportService transportService, AllocationService allocationService, ThreadPool threadPool ) { this.clusterService = clusterService; + this.transportService = transportService; this.nodeRemovalExecutor = new NodeRemovalClusterStateTaskExecutor(allocationService, logger); this.threadPool = threadPool; } + public void excludeDecommissionedNodesFromVotingConfig(Set nodes, ActionListener listener) { + transportService.sendRequest( + transportService.getLocalNode(), + AddVotingConfigExclusionsAction.NAME, + new AddVotingConfigExclusionsRequest(nodes.stream().toArray(String[] :: new)), + new TransportResponseHandler() { + @Override + public void handleResponse(AddVotingConfigExclusionsResponse response) { + listener.onResponse(null); + } + + @Override + public void handleException(TransportException exp) { + listener.onFailure(exp); + } + + @Override + public String executor() { + return ThreadPool.Names.SAME; + } + + @Override + public AddVotingConfigExclusionsResponse read(StreamInput in) throws IOException { + return new AddVotingConfigExclusionsResponse(in); + } + } + ); + } + + public void clearVotingConfigExclusion(ActionListener listener) { + final ClearVotingConfigExclusionsRequest clearVotingConfigExclusionsRequest = new ClearVotingConfigExclusionsRequest(); + clearVotingConfigExclusionsRequest.setWaitForRemoval(true); + transportService.sendRequest( + transportService.getLocalNode(), + ClearVotingConfigExclusionsAction.NAME, + clearVotingConfigExclusionsRequest, + new TransportResponseHandler() { + @Override + public void handleResponse(ClearVotingConfigExclusionsResponse response) { + logger.info("successfully cleared voting config after decommissioning"); + listener.onResponse(null); + } + + @Override + public void handleException(TransportException exp) { + logger.debug(new ParameterizedMessage("failure in clearing voting config exclusion after decommissioning"), exp); + listener.onFailure(exp); + } + + @Override + public String executor() { + return ThreadPool.Names.SAME; + } + + @Override + public ClearVotingConfigExclusionsResponse read(StreamInput in) throws IOException { + return new ClearVotingConfigExclusionsResponse(in); + } + } + ); + } + public void handleNodesDecommissionRequest( Set nodesToBeDecommissioned, String reason, diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index d935bc02b71d4..729ec944560d4 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -12,16 +12,11 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; import org.opensearch.action.ActionListener; -import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsAction; -import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsRequest; -import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsResponse; -import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsAction; -import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsRequest; -import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsResponse; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.NotClusterManagerException; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; +import org.opensearch.cluster.coordination.CoordinationMetadata; import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; @@ -38,14 +33,20 @@ import org.opensearch.transport.TransportException; import org.opensearch.transport.TransportResponseHandler; import org.opensearch.transport.TransportService; +import org.opensearch.cluster.coordination.CoordinationMetadata.VotingConfigExclusion; import java.io.IOException; -import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.function.Predicate; +import java.util.stream.Collectors; + +import static org.opensearch.cluster.routing.allocation.decider.AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING; +import static org.opensearch.cluster.routing.allocation.decider.AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_FORCE_GROUP_SETTING; /** * Service responsible for entire lifecycle of decommissioning and recommissioning an awareness attribute. @@ -72,6 +73,7 @@ public class DecommissionService { private final ThreadPool threadPool; private final DecommissionController decommissionController; private volatile List awarenessAttributes; + private volatile Map> forcedAwarenessAttributes; @Inject public DecommissionService( @@ -87,110 +89,100 @@ public DecommissionService( this.threadPool = threadPool; this.decommissionController = new DecommissionController( clusterService, + transportService, allocationService, threadPool ); - this.awarenessAttributes = AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING.get(settings); + this.awarenessAttributes = CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING.get(settings); clusterSettings.addSettingsUpdateConsumer( - AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING, + CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING, this::setAwarenessAttributes ); - } - List getAwarenessAttributes() { - return awarenessAttributes; + setForcedAwarenessAttributes(CLUSTER_ROUTING_ALLOCATION_AWARENESS_FORCE_GROUP_SETTING.get(settings)); + clusterSettings.addSettingsUpdateConsumer( + CLUSTER_ROUTING_ALLOCATION_AWARENESS_FORCE_GROUP_SETTING, + this::setForcedAwarenessAttributes + ); } private void setAwarenessAttributes(List awarenessAttributes) { this.awarenessAttributes = awarenessAttributes; } + private void setForcedAwarenessAttributes(Settings forceSettings) { + Map> forcedAwarenessAttributes = new HashMap<>(); + Map forceGroups = forceSettings.getAsGroups(); + for (Map.Entry entry : forceGroups.entrySet()) { + List aValues = entry.getValue().getAsList("values"); + if (aValues.size() > 0) { + forcedAwarenessAttributes.put(entry.getKey(), aValues); + } + } + this.forcedAwarenessAttributes = forcedAwarenessAttributes; + } + public void initiateAttributeDecommissioning( final DecommissionAttribute decommissionAttribute, final ActionListener listener, ClusterState state ) { - validateAwarenessAttribute(decommissionAttribute, getAwarenessAttributes()); + validateAwarenessAttribute(decommissionAttribute, awarenessAttributes, forcedAwarenessAttributes); logger.info("initiating awareness attribute [{}] decommissioning", decommissionAttribute.toString()); + + // remove all decommissioned cluster manager eligible nodes from voting config + // The method ensures that we don't exclude nodes multiple times excludeDecommissionedClusterManagerNodesFromVotingConfig(decommissionAttribute); - registerDecommissionAttribute(decommissionAttribute, listener); + + // explicitly throwing NotClusterManagerException as we can certainly say the local cluster manager node will + // be abdicated and soon will no longer be cluster manager. + if(transportService.getLocalNode().isClusterManagerNode() + && !nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute)) { + registerDecommissionAttribute(decommissionAttribute, listener); + } else { + throw new NotClusterManagerException( + "node [" + + transportService.getLocalNode().toString() + + "] not eligible to execute decommission request. Will retry until timeout." + ); + } } private void excludeDecommissionedClusterManagerNodesFromVotingConfig(DecommissionAttribute decommissionAttribute) { - final Predicate shouldDecommissionPredicate = discoveryNode -> nodeHasDecommissionedAttribute( - discoveryNode, - decommissionAttribute + Set clusterManagerNodesToBeDecommissioned = nodesWithDecommissionAttribute( + clusterService.state(), decommissionAttribute, true ); - List clusterManagerNodesToBeDecommissioned = new ArrayList<>(); - Iterator clusterManagerNodesIter = clusterService.state().nodes().getClusterManagerNodes().valuesIt(); - while (clusterManagerNodesIter.hasNext()) { - final DiscoveryNode node = clusterManagerNodesIter.next(); - if (shouldDecommissionPredicate.test(node)) { - clusterManagerNodesToBeDecommissioned.add(node.getName()); - } + Set clusterManagerNodesNameToBeDecommissioned = clusterManagerNodesToBeDecommissioned.stream() + .map(DiscoveryNode::getName) + .collect(Collectors.toSet()); + + Set currentVotingConfigExclusions = clusterService.getClusterApplierService().state().coordinationMetadata().getVotingConfigExclusions(); + Set excludedNodesName = currentVotingConfigExclusions.stream().map(VotingConfigExclusion::getNodeName).collect(Collectors.toSet()); + + // check if the to-be-excluded nodes are excluded. If yes, we don't need to exclude them again + if (clusterManagerNodesNameToBeDecommissioned.size() == 0 + || (clusterManagerNodesNameToBeDecommissioned.size() == excludedNodesName.size() + && excludedNodesName.containsAll(clusterManagerNodesNameToBeDecommissioned))) { + return; } - transportService.sendRequest( - transportService.getLocalNode(), - AddVotingConfigExclusionsAction.NAME, - new AddVotingConfigExclusionsRequest(clusterManagerNodesToBeDecommissioned.toArray(String[]::new)), - new TransportResponseHandler() { + decommissionController.excludeDecommissionedNodesFromVotingConfig( + clusterManagerNodesNameToBeDecommissioned, + new ActionListener() { @Override - public void handleResponse(AddVotingConfigExclusionsResponse response) { + public void onResponse(Void unused) { logger.info( - "successfully removed decommissioned cluster manager eligible nodes [{}] from voting config, " - + "proceeding to drain the decommissioned nodes", - clusterManagerNodesToBeDecommissioned.toString() + "successfully removed decommissioned cluster manager eligible nodes [{}] from voting config " + , clusterManagerNodesToBeDecommissioned.toString() ); } @Override - public void handleException(TransportException exp) { + public void onFailure(Exception e) { logger.debug( new ParameterizedMessage("failure in removing decommissioned cluster manager eligible nodes from voting config"), - exp + e ); } - - @Override - public String executor() { - return ThreadPool.Names.SAME; - } - - @Override - public AddVotingConfigExclusionsResponse read(StreamInput in) throws IOException { - return new AddVotingConfigExclusionsResponse(in); - } - } - ); - } - - private void clearVotingConfigAfterSuccessfulDecommission() { - final ClearVotingConfigExclusionsRequest clearVotingConfigExclusionsRequest = new ClearVotingConfigExclusionsRequest(); - clearVotingConfigExclusionsRequest.setWaitForRemoval(true); - transportService.sendRequest( - transportService.getLocalNode(), - ClearVotingConfigExclusionsAction.NAME, - clearVotingConfigExclusionsRequest, - new TransportResponseHandler() { - @Override - public void handleResponse(ClearVotingConfigExclusionsResponse response) { - logger.info("successfully cleared voting config after decommissioning"); - } - - @Override - public void handleException(TransportException exp) { - logger.debug(new ParameterizedMessage("failure in clearing voting config exclusion after decommissioning"), exp); - } - - @Override - public String executor() { - return ThreadPool.Names.SAME; - } - - @Override - public ClearVotingConfigExclusionsResponse read(StreamInput in) throws IOException { - return new ClearVotingConfigExclusionsResponse(in); - } } ); } @@ -210,14 +202,6 @@ private void registerDecommissionAttribute( final DecommissionAttribute decommissionAttribute, final ActionListener listener ) { - if (!transportService.getLocalNode().isClusterManagerNode() - || nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute)) { - throw new NotClusterManagerException( - "node [" - + transportService.getLocalNode().toString() - + "] not eligible to execute decommission request. Will retry until timeout." - ); - } clusterService.submitStateUpdateTask( "put_decommission [" + decommissionAttribute + "]", new ClusterStateUpdateTask(Priority.URGENT) { @@ -328,7 +312,7 @@ public void onFailure(Exception e) { // execute nodes decommissioning and wait for it to complete decommissionController.handleNodesDecommissionRequest( - nodesWithDecommissionAttribute(state, decommissionAttribute), + nodesWithDecommissionAttribute(state, decommissionAttribute, false), "nodes-decommissioned", TimeValue.timeValueSeconds(30L), new ActionListener() { @@ -349,20 +333,25 @@ public void onFailure(Exception e) { } } ); - clearVotingConfigAfterSuccessfulDecommission(); +// decommissionController.clearVotingConfigExclusion(); } - - private Set nodesWithDecommissionAttribute(ClusterState clusterState, DecommissionAttribute decommissionAttribute) { + public Set nodesWithDecommissionAttribute( + ClusterState clusterState, + DecommissionAttribute decommissionAttribute, + boolean onlyClusterManagerNodes + ) { Set nodesWithDecommissionAttribute = new HashSet<>(); - final Predicate shouldRemoveNodePredicate = discoveryNode -> nodeHasDecommissionedAttribute( + final Predicate shouldDecommissionNodePredicate = discoveryNode -> nodeHasDecommissionedAttribute( discoveryNode, decommissionAttribute ); - Iterator nodesIter = clusterState.nodes().getNodes().valuesIt(); + Iterator nodesIter = onlyClusterManagerNodes? clusterState.nodes().getClusterManagerNodes().valuesIt() : + clusterState.nodes().getNodes().valuesIt(); + while (nodesIter.hasNext()) { final DiscoveryNode node = nodesIter.next(); - if (shouldRemoveNodePredicate.test(node)) { + if (shouldDecommissionNodePredicate.test(node)) { nodesWithDecommissionAttribute.add(node); } } @@ -373,11 +362,22 @@ private static boolean nodeHasDecommissionedAttribute(DiscoveryNode discoveryNod return discoveryNode.getAttributes().get(decommissionAttribute.attributeName()).equals(decommissionAttribute.attributeValue()); } - private static void validateAwarenessAttribute(final DecommissionAttribute decommissionAttribute, List awarenessAttributes) { + private static void validateAwarenessAttribute( + final DecommissionAttribute decommissionAttribute, + List awarenessAttributes, + Map> forcedAwarenessAttributes) { + if (awarenessAttributes == null || forcedAwarenessAttributes == null) { + throw new DecommissionFailedException(decommissionAttribute, "awareness attribute and forced awareness attribute not set to the cluster."); + } if (!awarenessAttributes.contains(decommissionAttribute.attributeName())) { throw new DecommissionFailedException(decommissionAttribute, "invalid awareness attribute requested for decommissioning"); } - // TODO - should attribute value be part of force zone values? If yes, read setting and throw exception if not found + if (!forcedAwarenessAttributes.get(decommissionAttribute.attributeName()).contains(decommissionAttribute.attributeValue()) ) { + throw new DecommissionFailedException( + decommissionAttribute, + "invalid awareness attribute value requested for decommissioning. Set forced awareness values before to decommission" + ); + } } private static void ensureNoAwarenessAttributeDecommissioned( diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java index 809a392d060e2..0f7f76fc5eeec 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java @@ -15,13 +15,19 @@ import org.opensearch.OpenSearchTimeoutException; import org.opensearch.Version; import org.opensearch.action.ActionListener; +import org.opensearch.action.admin.cluster.configuration.TransportAddVotingConfigExclusionsAction; import org.opensearch.action.admin.cluster.configuration.TransportAddVotingConfigExclusionsActionTests; +import org.opensearch.action.admin.cluster.configuration.TransportClearVotingConfigExclusionsAction; +import org.opensearch.action.support.ActionFilters; import org.opensearch.cluster.ClusterName; import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.coordination.CoordinationMetadata.VotingConfigExclusion; import org.opensearch.cluster.ClusterStateObserver; import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; +import org.opensearch.cluster.coordination.CoordinationMetadata; import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.node.DiscoveryNodeRole; @@ -29,15 +35,21 @@ import org.opensearch.cluster.routing.allocation.AllocationService; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.collect.ImmutableOpenMap; +import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; +import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.test.OpenSearchTestCase; +import org.opensearch.test.transport.MockTransport; import org.opensearch.threadpool.TestThreadPool; import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportService; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; @@ -49,6 +61,7 @@ import static java.util.Collections.emptySet; import static java.util.Collections.singletonMap; import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.sameInstance; import static org.hamcrest.Matchers.startsWith; @@ -58,6 +71,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.opensearch.action.admin.cluster.configuration.TransportAddVotingConfigExclusionsAction.MAXIMUM_VOTING_CONFIG_EXCLUSIONS_SETTING; import static org.opensearch.cluster.ClusterState.builder; import static org.opensearch.cluster.OpenSearchAllocationTestCase.createAllocationService; import static org.opensearch.test.ClusterServiceUtils.createClusterService; @@ -67,8 +81,10 @@ public class DecommissionControllerTests extends OpenSearchTestCase { private ThreadPool threadPool; private ClusterService clusterService; + private TransportService transportService; private AllocationService allocationService; private DecommissionController decommissionController; + private ClusterSettings clusterSettings; @Override public void setUp() throws Exception { @@ -79,7 +95,7 @@ public void setUp() throws Exception { } @Before - public void setDefaultClusterState() { + public void setTransportServiceAndDefaultClusterState() { ClusterState clusterState = ClusterState.builder(new ClusterName("test")).build(); logger.info("--> adding five nodes on same zone_1"); clusterState = addNodes(clusterState, "zone_1", "node1", "node2", "node3", "node4", "node5"); @@ -88,12 +104,47 @@ public void setDefaultClusterState() { logger.info("--> adding five nodes on same zone_3"); clusterState = addNodes(clusterState, "zone_3", "node11", "node12", "node13", "node14", "node15"); clusterState = setLocalNodeAsClusterManagerNode(clusterState, "node1"); + clusterState = setThreeNodesInVotingConfig(clusterState); final ClusterState.Builder builder = builder(clusterState); setState( clusterService, builder ); - decommissionController = new DecommissionController(clusterService, allocationService, threadPool); + final MockTransport transport = new MockTransport(); + transportService = transport.createTransportService( + Settings.EMPTY, + threadPool, + TransportService.NOOP_TRANSPORT_INTERCEPTOR, + boundTransportAddress -> clusterService.state().nodes().get("node1"), + null, + emptySet() + ); + + final Settings.Builder nodeSettingsBuilder = Settings.builder(); + final Settings nodeSettings = nodeSettingsBuilder.build(); + clusterSettings = new ClusterSettings(nodeSettings, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS); + + new TransportAddVotingConfigExclusionsAction( + nodeSettings, + clusterSettings, + transportService, + clusterService, + threadPool, + new ActionFilters(emptySet()), + new IndexNameExpressionResolver(new ThreadContext(Settings.EMPTY)) + ); // registers action + + new TransportClearVotingConfigExclusionsAction( + transportService, + clusterService, + threadPool, + new ActionFilters(emptySet()), + new IndexNameExpressionResolver(new ThreadContext(Settings.EMPTY)) + ); // registers action + + transportService.start(); + transportService.acceptIncomingRequests(); + decommissionController = new DecommissionController(clusterService, transportService, allocationService, threadPool); } @After @@ -102,6 +153,48 @@ public void shutdownThreadPoolAndClusterService() { threadPool.shutdown(); } + public void testAddNodesToVotingConfigExclusion() throws InterruptedException { + final CountDownLatch countDownLatch = new CountDownLatch(1); + Set nodesToRemoveFromVotingConfig = Collections.singleton(randomFrom("node1", "node6", "node11")); + decommissionController.excludeDecommissionedNodesFromVotingConfig( + nodesToRemoveFromVotingConfig, + new ActionListener() { + @Override + public void onResponse(Void unused) { + countDownLatch.countDown(); + } + + @Override + public void onFailure(Exception e) { + fail("unexpected failure occurred while removing node from voting config " + e); + } + } + ); + assertTrue(countDownLatch.await(30, TimeUnit.SECONDS)); + clusterService.getClusterApplierService().state().getVotingConfigExclusions().forEach(vce -> { + assertTrue(nodesToRemoveFromVotingConfig.contains(vce.getNodeName())); + assertEquals(nodesToRemoveFromVotingConfig.size(), 1); + }); + } + + public void testClearVotingConfigExclusions() throws InterruptedException { + final CountDownLatch countDownLatch = new CountDownLatch(1); + decommissionController.clearVotingConfigExclusion(new ActionListener() { + @Override + public void onResponse(Void unused) { + countDownLatch.countDown(); + } + + @Override + public void onFailure(Exception e) { + fail("unexpected failure occurred while clearing voting config exclusion" + e); + } + }); + assertTrue(countDownLatch.await(30, TimeUnit.SECONDS)); + assertThat(clusterService.getClusterApplierService().state().getVotingConfigExclusions(), empty()); + } + + public void testNodesRemovedForDecommissionRequestSuccessfulResponse() throws InterruptedException { final CountDownLatch countDownLatch = new CountDownLatch(1); Set nodesToBeRemoved = new HashSet<>(); @@ -217,6 +310,21 @@ private ClusterState setLocalNodeAsClusterManagerNode(ClusterState clusterState, return clusterState; } + private ClusterState setThreeNodesInVotingConfig(ClusterState clusterState) { + final CoordinationMetadata.VotingConfiguration votingConfiguration = CoordinationMetadata.VotingConfiguration.of( + clusterState.nodes().get("node1"), clusterState.nodes().get("node6"), clusterState.nodes().get("node11") + ); + + Metadata.Builder builder = Metadata.builder().coordinationMetadata( + CoordinationMetadata.builder() + .lastAcceptedConfiguration(votingConfiguration) + .lastCommittedConfiguration(votingConfiguration) + .build() + ); + clusterState = ClusterState.builder(clusterState).metadata(builder).build(); + return clusterState; + } + private static DiscoveryNode newNode(String nodeId, Map attributes) { return new DiscoveryNode(nodeId, buildNewFakeTransportAddress(), attributes, CLUSTER_MANAGER_DATA_ROLE, Version.CURRENT); } diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java index ea4aee984df98..f2baea304344a 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java @@ -8,7 +8,179 @@ package org.opensearch.cluster.decommission; +import org.hamcrest.Matchers; +import org.junit.After; +import org.junit.Before; +import org.opensearch.Version; +import org.opensearch.action.ActionListener; +import org.opensearch.action.search.CreatePitController; +import org.opensearch.cluster.ClusterName; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.ClusterStateObserver; +import org.opensearch.cluster.ack.ClusterStateUpdateResponse; +import org.opensearch.cluster.coordination.CoordinationMetadata; +import org.opensearch.cluster.metadata.Metadata; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.node.DiscoveryNodeRole; +import org.opensearch.cluster.node.DiscoveryNodes; +import org.opensearch.cluster.routing.allocation.AllocationService; +import org.opensearch.cluster.routing.allocation.decider.AwarenessAllocationDecider; +import org.opensearch.cluster.routing.allocation.decider.ClusterRebalanceAllocationDecider; +import org.opensearch.cluster.routing.allocation.decider.ThrottlingAllocationDecider; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.settings.ClusterSettings; +import org.opensearch.common.settings.Settings; import org.opensearch.test.OpenSearchTestCase; +import org.opensearch.test.transport.MockTransport; +import org.opensearch.threadpool.TestThreadPool; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportService; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import static java.util.Collections.emptySet; +import static java.util.Collections.singletonMap; +import static org.mockito.ArgumentMatchers.startsWith; +import static org.mockito.Mockito.mock; +import static org.opensearch.cluster.ClusterState.builder; +import static org.opensearch.cluster.OpenSearchAllocationTestCase.createAllocationService; +import static org.opensearch.cluster.coordination.NoClusterManagerBlockService.NO_CLUSTER_MANAGER_BLOCK_SETTING; +import static org.opensearch.cluster.routing.allocation.decider.AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING; +import static org.opensearch.test.ClusterServiceUtils.createClusterService; +import static org.opensearch.test.ClusterServiceUtils.setState; public class DecommissionServiceTests extends OpenSearchTestCase { + + private ThreadPool threadPool; + private ClusterService clusterService; + private TransportService transportService; + private AllocationService allocationService; + private DecommissionService decommissionService; + private ClusterSettings clusterSettings; + + @Override + public void setUp() throws Exception { + super.setUp(); + super.setUp(); + threadPool = new TestThreadPool("test", Settings.EMPTY); + clusterService = createClusterService(threadPool); + allocationService = createAllocationService(); + } + + @Before + public void setUpService() { + ClusterState clusterState = ClusterState.builder(new ClusterName("test")).build(); + logger.info("--> adding five nodes on same zone_1"); + clusterState = addNodes(clusterState, "zone_1", "node1", "node2", "node3", "node4", "node5"); + logger.info("--> adding five nodes on same zone_2"); + clusterState = addNodes(clusterState, "zone_2", "node6", "node7", "node8", "node9", "node10"); + logger.info("--> adding five nodes on same zone_3"); + clusterState = addNodes(clusterState, "zone_3", "node11", "node12", "node13", "node14", "node15"); + clusterState = setLocalNodeAsClusterManagerNode(clusterState, "node1"); + clusterState = setThreeNodesInVotingConfig(clusterState); + final ClusterState.Builder builder = builder(clusterState); + setState( + clusterService, + builder + ); + final MockTransport transport = new MockTransport(); + transportService = transport.createTransportService( + Settings.EMPTY, + threadPool, + TransportService.NOOP_TRANSPORT_INTERCEPTOR, + boundTransportAddress -> clusterService.state().nodes().get("node1"), + null, + emptySet() + ); + + final Settings.Builder nodeSettingsBuilder = Settings.builder() + .put(AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING.getKey(), "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "zone_1,zone_2,zone_3"); + + clusterSettings = new ClusterSettings(nodeSettingsBuilder.build(), ClusterSettings.BUILT_IN_CLUSTER_SETTINGS); + transportService.start(); + transportService.acceptIncomingRequests(); + + this.decommissionService = new DecommissionService( + nodeSettingsBuilder.build(), + clusterSettings, + clusterService, + transportService, + threadPool, + allocationService + ); + } + + @After + public void shutdownThreadPoolAndClusterService() { + clusterService.stop(); + threadPool.shutdown(); + } + + @SuppressWarnings("unchecked") + public void testDecommissioningNotInitiatedForInvalidAttributeName() { + DecommissionAttribute decommissionAttribute = new DecommissionAttribute("rack", "rack-a"); + ActionListener listener = mock(ActionListener.class); + DecommissionFailedException e = expectThrows(DecommissionFailedException.class, () -> { + decommissionService.initiateAttributeDecommissioning( + decommissionAttribute, listener, clusterService.state()); + }); + assertThat(e.getMessage(), Matchers.endsWith("invalid awareness attribute requested for decommissioning")); + } + + @SuppressWarnings("unchecked") + public void testDecommissioningNotInitiatedForInvalidAttributeValue() { + DecommissionAttribute decommissionAttribute = new DecommissionAttribute("zone", "random"); + ActionListener listener = mock(ActionListener.class); + DecommissionFailedException e = expectThrows(DecommissionFailedException.class, () -> { + decommissionService.initiateAttributeDecommissioning( + decommissionAttribute, listener, clusterService.state()); + }); + assertThat( + e.getMessage(), + Matchers.endsWith("invalid awareness attribute value requested for decommissioning. Set forced awareness values before to decommission") + ); + } + + private ClusterState addNodes(ClusterState clusterState, String zone, String... nodeIds) { + DiscoveryNodes.Builder nodeBuilder = DiscoveryNodes.builder(clusterState.nodes()); + org.opensearch.common.collect.List.of(nodeIds).forEach(nodeId -> nodeBuilder.add(newNode(nodeId, singletonMap("zone", zone)))); + clusterState = ClusterState.builder(clusterState).nodes(nodeBuilder).build(); + return clusterState; + } + + private ClusterState setLocalNodeAsClusterManagerNode(ClusterState clusterState, String nodeId) { + DiscoveryNodes.Builder nodeBuilder = DiscoveryNodes.builder(clusterState.nodes()); + nodeBuilder.localNodeId(nodeId); + nodeBuilder.clusterManagerNodeId(nodeId); + clusterState = ClusterState.builder(clusterState).nodes(nodeBuilder).build(); + return clusterState; + } + + private ClusterState setThreeNodesInVotingConfig(ClusterState clusterState) { + final CoordinationMetadata.VotingConfiguration votingConfiguration = CoordinationMetadata.VotingConfiguration.of( + clusterState.nodes().get("node1"), clusterState.nodes().get("node6"), clusterState.nodes().get("node11") + ); + + Metadata.Builder builder = Metadata.builder().coordinationMetadata( + CoordinationMetadata.builder() + .lastAcceptedConfiguration(votingConfiguration) + .lastCommittedConfiguration(votingConfiguration) + .build() + ); + clusterState = ClusterState.builder(clusterState).metadata(builder).build(); + return clusterState; + } + + private static DiscoveryNode newNode(String nodeId, Map attributes) { + return new DiscoveryNode(nodeId, buildNewFakeTransportAddress(), attributes, CLUSTER_MANAGER_DATA_ROLE, Version.CURRENT); + } + + final private static Set CLUSTER_MANAGER_DATA_ROLE = Collections.unmodifiableSet( + new HashSet<>(Arrays.asList(DiscoveryNodeRole.CLUSTER_MANAGER_ROLE, DiscoveryNodeRole.DATA_ROLE)) + ); } From 3bf56bead660016d0662bfd9c5167ec6b72d8827 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 30 Aug 2022 12:54:53 +0530 Subject: [PATCH 050/187] Add UT Signed-off-by: Rishab Nahata --- .../decommission/DecommissionService.java | 38 ++++++++++++------- .../DecommissionServiceTests.java | 25 ++++++++++++ 2 files changed, 50 insertions(+), 13 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 729ec944560d4..5e86954cc18b2 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -128,10 +128,13 @@ public void initiateAttributeDecommissioning( ClusterState state ) { validateAwarenessAttribute(decommissionAttribute, awarenessAttributes, forcedAwarenessAttributes); + DecommissionAttributeMetadata decommissionAttributeMetadata = state.metadata().custom(DecommissionAttributeMetadata.TYPE); + ensureNoAwarenessAttributeDecommissioned(decommissionAttributeMetadata, decommissionAttribute); + logger.info("initiating awareness attribute [{}] decommissioning", decommissionAttribute.toString()); // remove all decommissioned cluster manager eligible nodes from voting config - // The method ensures that we don't exclude nodes multiple times + // The method ensures that we don't exclude same nodes multiple times excludeDecommissionedClusterManagerNodesFromVotingConfig(decommissionAttribute); // explicitly throwing NotClusterManagerException as we can certainly say the local cluster manager node will @@ -190,10 +193,10 @@ public void onFailure(Exception e) { /** * Registers new decommissioned attribute metadata in the cluster state with {@link DecommissionStatus#DECOMMISSION_INIT} *

- * This method can be only called on the cluster-manager node. It tries to create a new decommissioned attribute on the master + * This method can be only called on the cluster-manager node. It tries to create a new decommissioned attribute on the cluster manager * and if it was successful it adds new decommissioned attribute to cluster metadata. *

- * This method ensures that request is performed only on eligible cluster manager node + * This method would only be executed on eligible cluster manager node * * @param decommissionAttribute register decommission attribute in the metadata request * @param listener register decommission listener @@ -365,18 +368,27 @@ private static boolean nodeHasDecommissionedAttribute(DiscoveryNode discoveryNod private static void validateAwarenessAttribute( final DecommissionAttribute decommissionAttribute, List awarenessAttributes, - Map> forcedAwarenessAttributes) { - if (awarenessAttributes == null || forcedAwarenessAttributes == null) { - throw new DecommissionFailedException(decommissionAttribute, "awareness attribute and forced awareness attribute not set to the cluster."); + Map> forcedAwarenessAttributes + ) { + String msg = null; + if (awarenessAttributes == null) { + msg = "awareness attribute not set to the cluster."; } - if (!awarenessAttributes.contains(decommissionAttribute.attributeName())) { - throw new DecommissionFailedException(decommissionAttribute, "invalid awareness attribute requested for decommissioning"); + else if (forcedAwarenessAttributes == null) { + msg = "forced awareness attribute not set to the cluster."; } - if (!forcedAwarenessAttributes.get(decommissionAttribute.attributeName()).contains(decommissionAttribute.attributeValue()) ) { - throw new DecommissionFailedException( - decommissionAttribute, - "invalid awareness attribute value requested for decommissioning. Set forced awareness values before to decommission" - ); + else if (!awarenessAttributes.contains(decommissionAttribute.attributeName())) { + msg = "invalid awareness attribute requested for decommissioning"; + } + else if (!forcedAwarenessAttributes.containsKey(decommissionAttribute.attributeName())) { + msg = "forced awareness attribute [" + forcedAwarenessAttributes.toString() + "] doesn't have the decommissioning attribute"; + } + else if (!forcedAwarenessAttributes.get(decommissionAttribute.attributeName()).contains(decommissionAttribute.attributeValue()) ) { + msg = "invalid awareness attribute value requested for decommissioning. Set forced awareness values before to decommission"; + } + + if (msg != null) { + throw new DecommissionFailedException(decommissionAttribute, msg); } } diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java index f2baea304344a..5f55387745ca0 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java @@ -19,6 +19,7 @@ import org.opensearch.cluster.ClusterStateObserver; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; import org.opensearch.cluster.coordination.CoordinationMetadata; +import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.node.DiscoveryNodeRole; @@ -146,6 +147,30 @@ public void testDecommissioningNotInitiatedForInvalidAttributeValue() { ); } + @SuppressWarnings("unchecked") + public void testDecommissioningNotInitiatedWhenAlreadyDecommissioned() { + DecommissionAttributeMetadata oldMetadata = new DecommissionAttributeMetadata( + new DecommissionAttribute("zone", "zone_1"), + DecommissionStatus.DECOMMISSION_IN_PROGRESS + ); + final ClusterState.Builder builder = builder(clusterService.state()); + setState( + clusterService, + builder.metadata( + Metadata.builder( + clusterService.state().metadata()).putCustom(DecommissionAttributeMetadata.TYPE, oldMetadata).build() + )); + ActionListener listener = mock(ActionListener.class); + DecommissionFailedException e = expectThrows(DecommissionFailedException.class, () -> { + decommissionService.initiateAttributeDecommissioning( + new DecommissionAttribute("zone", "zone_2"), listener, clusterService.state()); + }); + assertThat( + e.getMessage(), + Matchers.endsWith("one awareness attribute already decommissioned, recommission before triggering another decommission") + ); + } + private ClusterState addNodes(ClusterState clusterState, String zone, String... nodeIds) { DiscoveryNodes.Builder nodeBuilder = DiscoveryNodes.builder(clusterState.nodes()); org.opensearch.common.collect.List.of(nodeIds).forEach(nodeId -> nodeBuilder.add(newNode(nodeId, singletonMap("zone", zone)))); From 91ab1e3afba4c17be6c63c23448c45b2d5500573 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 30 Aug 2022 13:02:59 +0530 Subject: [PATCH 051/187] Fix decommission initiation Signed-off-by: Rishab Nahata --- .../opensearch/cluster/decommission/DecommissionService.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 5e86954cc18b2..8ffa388bf414f 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -127,8 +127,11 @@ public void initiateAttributeDecommissioning( final ActionListener listener, ClusterState state ) { + // validates if the correct awareness attributes and forced awareness attribute set to the cluster before initiating decommission action validateAwarenessAttribute(decommissionAttribute, awarenessAttributes, forcedAwarenessAttributes); + DecommissionAttributeMetadata decommissionAttributeMetadata = state.metadata().custom(DecommissionAttributeMetadata.TYPE); + // validates that there's no inflight decommissioning or already executed decommission in place ensureNoAwarenessAttributeDecommissioned(decommissionAttributeMetadata, decommissionAttribute); logger.info("initiating awareness attribute [{}] decommissioning", decommissionAttribute.toString()); @@ -168,6 +171,7 @@ private void excludeDecommissionedClusterManagerNodesFromVotingConfig(Decommissi && excludedNodesName.containsAll(clusterManagerNodesNameToBeDecommissioned))) { return; } + // send a transport request to exclude to-be-decommissioned cluster manager eligible nodes from voting config decommissionController.excludeDecommissionedNodesFromVotingConfig( clusterManagerNodesNameToBeDecommissioned, new ActionListener() { From bd1c49c6e4db2a3b80d51f93d6257f67468d5555 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 30 Aug 2022 14:17:59 +0530 Subject: [PATCH 052/187] Changes Signed-off-by: Rishab Nahata --- .../decommission/DecommissionService.java | 118 +++++++++--------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 8ffa388bf414f..16c77a758a433 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -214,43 +214,37 @@ private void registerDecommissionAttribute( new ClusterStateUpdateTask(Priority.URGENT) { @Override public ClusterState execute(ClusterState currentState) { - logger.info( - "registering decommission metadata for attribute [{}] with status as [{}]", - decommissionAttribute.toString(), - DecommissionStatus.DECOMMISSION_INIT - ); Metadata metadata = currentState.metadata(); Metadata.Builder mdBuilder = Metadata.builder(metadata); DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.custom(DecommissionAttributeMetadata.TYPE); ensureNoAwarenessAttributeDecommissioned(decommissionAttributeMetadata, decommissionAttribute); decommissionAttributeMetadata = new DecommissionAttributeMetadata(decommissionAttribute); mdBuilder.putCustom(DecommissionAttributeMetadata.TYPE, decommissionAttributeMetadata); + logger.info( + "registering decommission metadata for attribute [{}] with status as [{}]", + decommissionAttribute.toString(), + DecommissionStatus.DECOMMISSION_INIT + ); return ClusterState.builder(currentState).metadata(mdBuilder).build(); } @Override public void onFailure(String source, Exception e) { if (e instanceof DecommissionFailedException) { - logger.error( - () -> new ParameterizedMessage("failed to decommission attribute [{}]", decommissionAttribute.toString()), - e - ); + logger.error(() -> new ParameterizedMessage("failed to decommission attribute [{}]", decommissionAttribute.toString()), e); listener.onFailure(e); } else if (e instanceof NotClusterManagerException) { logger.debug( () -> new ParameterizedMessage( "cluster-manager updated while executing request for decommission attribute [{}]", decommissionAttribute.toString() - ), - e + ), e ); + // we don't want to send the failure response to the listener here as the request will be retried } else { - logger.error( - () -> new ParameterizedMessage( - "failed to initiate decommissioning for attribute [{}]", - decommissionAttribute.toString() - ), - e + logger.error(() -> new ParameterizedMessage( + "failed to initiate decommissioning for attribute [{}]", decommissionAttribute.toString() + ), e ); listener.onFailure(e); } @@ -270,30 +264,29 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS } private void initiateGracefulDecommission() { - // maybe create a supplier for status update listener? - ActionListener listener = new ActionListener<>() { - @Override - public void onResponse(Void unused) { - logger.info( - "updated decommission status to [{}], weighing away awareness attribute for graceful shutdown", - DecommissionStatus.DECOMMISSION_IN_PROGRESS - ); - failDecommissionedNodes(clusterService.getClusterApplierService().state()); - } - - @Override - public void onFailure(Exception e) { - logger.error( - () -> new ParameterizedMessage( - "failed to update decommission status to [{}], will not proceed with decommission", + decommissionController.updateMetadataWithDecommissionStatus( + DecommissionStatus.DECOMMISSION_IN_PROGRESS, + new ActionListener() { + @Override + public void onResponse(Void unused) { + logger.info( + "updated decommission status to [{}], weighing away awareness attribute for graceful shutdown", DecommissionStatus.DECOMMISSION_IN_PROGRESS - ), - e - ); + ); + // TODO - should trigger weigh away here and on successful weigh away -> fail the decommissioned nodes + failDecommissionedNodes(clusterService.getClusterApplierService().state()); + } + + @Override + public void onFailure(Exception e) { + logger.error(() -> new ParameterizedMessage( + "failed to update decommission status to [{}], will not proceed with decommission", + DecommissionStatus.DECOMMISSION_IN_PROGRESS + ), e + ); + } } - }; - decommissionController.updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_IN_PROGRESS, listener); - // TODO - code for graceful decommission + ); } private void failDecommissionedNodes(ClusterState state) { @@ -302,13 +295,31 @@ private void failDecommissionedNodes(ClusterState state) { : "unexpected status encountered while decommissioning nodes"; DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute(); - ActionListener statusUpdateListener = new ActionListener<>() { + // execute nodes decommissioning + decommissionController.handleNodesDecommissionRequest( + nodesWithDecommissionAttribute(state, decommissionAttribute, false), + "nodes-decommissioned", + TimeValue.timeValueSeconds(30L), // TODO - read timeout from request while integrating with API + new ActionListener() { + @Override + public void onResponse(Void unused) { + clearVotingConfigExclusionAndUpdateStatus(true); + } + + @Override + public void onFailure(Exception e) { + clearVotingConfigExclusionAndUpdateStatus(false); + } + } + ); + } + + private void clearVotingConfigExclusionAndUpdateStatus(boolean decommissionSuccessful) { + ActionListener statusUpdateListener = new ActionListener() { @Override public void onResponse(Void unused) { - logger.info( - "updated decommission status to [{}], decommissioning completed.", - DecommissionStatus.DECOMMISSION_SUCCESSFUL - ); + logger.info("successful updated decommission status with [{}]", + decommissionSuccessful ? DecommissionStatus.DECOMMISSION_SUCCESSFUL : DecommissionStatus.DECOMMISSION_FAILED); } @Override @@ -316,31 +327,20 @@ public void onFailure(Exception e) { logger.error("failed to update the decommission status"); } }; - - // execute nodes decommissioning and wait for it to complete - decommissionController.handleNodesDecommissionRequest( - nodesWithDecommissionAttribute(state, decommissionAttribute, false), - "nodes-decommissioned", - TimeValue.timeValueSeconds(30L), + decommissionController.clearVotingConfigExclusion( new ActionListener() { @Override public void onResponse(Void unused) { - decommissionController.updateMetadataWithDecommissionStatus( - DecommissionStatus.DECOMMISSION_SUCCESSFUL, - statusUpdateListener - ); + DecommissionStatus updateStatusWith = decommissionSuccessful? DecommissionStatus.DECOMMISSION_SUCCESSFUL : DecommissionStatus.DECOMMISSION_FAILED; + decommissionController.updateMetadataWithDecommissionStatus(updateStatusWith, statusUpdateListener); } @Override public void onFailure(Exception e) { - decommissionController.updateMetadataWithDecommissionStatus( - DecommissionStatus.DECOMMISSION_FAILED, - statusUpdateListener - ); + decommissionController.updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_FAILED, statusUpdateListener); } } ); -// decommissionController.clearVotingConfigExclusion(); } public Set nodesWithDecommissionAttribute( From 623aa1295adb8dcb404631343e264166db17de2b Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 30 Aug 2022 15:09:26 +0530 Subject: [PATCH 053/187] Move DecommissionAttributeMetadata to decommission package Signed-off-by: Rishab Nahata --- .../java/org/opensearch/cluster/ClusterModule.java | 2 +- .../cluster/coordination/JoinTaskExecutor.java | 6 +++--- .../DecommissionAttributeMetadata.java | 5 +++-- .../cluster/decommission/DecommissionController.java | 1 - .../cluster/decommission/DecommissionService.java | 1 - .../cluster/coordination/JoinTaskExecutorTests.java | 12 +++++------- .../decommission/DecommissionControllerTests.java | 1 - .../decommission/DecommissionServiceTests.java | 3 +-- ...ommissionAttributeMetadataSerializationTests.java | 1 + .../metadata/DecommissionAttributeMetadataTests.java | 1 + .../DecommissionAttributeMetadataXContentTests.java | 1 + 11 files changed, 16 insertions(+), 18 deletions(-) rename server/src/main/java/org/opensearch/cluster/{metadata => decommission}/DecommissionAttributeMetadata.java (98%) diff --git a/server/src/main/java/org/opensearch/cluster/ClusterModule.java b/server/src/main/java/org/opensearch/cluster/ClusterModule.java index 115b9bdf3d8d6..892e65e2ee5b4 100644 --- a/server/src/main/java/org/opensearch/cluster/ClusterModule.java +++ b/server/src/main/java/org/opensearch/cluster/ClusterModule.java @@ -35,10 +35,10 @@ import org.opensearch.cluster.action.index.MappingUpdatedAction; import org.opensearch.cluster.action.index.NodeMappingRefreshAction; import org.opensearch.cluster.action.shard.ShardStateAction; +import org.opensearch.cluster.decommission.DecommissionAttributeMetadata; import org.opensearch.cluster.metadata.ComponentTemplateMetadata; import org.opensearch.cluster.metadata.ComposableIndexTemplateMetadata; import org.opensearch.cluster.metadata.DataStreamMetadata; -import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; import org.opensearch.cluster.metadata.IndexGraveyard; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.metadata.Metadata; diff --git a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java index 7008474222aef..a104499109683 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java @@ -40,9 +40,9 @@ import org.opensearch.cluster.NotClusterManagerException; import org.opensearch.cluster.block.ClusterBlocks; import org.opensearch.cluster.decommission.DecommissionAttribute; +import org.opensearch.cluster.decommission.DecommissionAttributeMetadata; import org.opensearch.cluster.decommission.DecommissionStatus; import org.opensearch.cluster.decommission.NodeDecommissionedException; -import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; @@ -476,7 +476,7 @@ public static void ensureMajorVersionBarrier(Version joiningNodeVersion, Version } } - public static void ensureNodeNotDecommissioned(DiscoveryNode node, Metadata metadata) { + public static void ensureNodeCommissioned(DiscoveryNode node, Metadata metadata) { DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.custom(DecommissionAttributeMetadata.TYPE); if (decommissionAttributeMetadata != null) { DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute(); @@ -506,7 +506,7 @@ public static Collection> addBuiltInJoin validators.add((node, state) -> { ensureNodesCompatibility(node.getVersion(), state.getNodes()); ensureIndexCompatibility(node.getVersion(), state.getMetadata()); - ensureNodeNotDecommissioned(node, state.getMetadata()); + ensureNodeCommissioned(node, state.getMetadata()); }); validators.addAll(onJoinValidators); return Collections.unmodifiableCollection(validators); diff --git a/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttributeMetadata.java similarity index 98% rename from server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java rename to server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttributeMetadata.java index 2034ab34e25c3..72bdfbdca78d3 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttributeMetadata.java @@ -6,7 +6,7 @@ * compatible open source license. */ -package org.opensearch.cluster.metadata; +package org.opensearch.cluster.decommission; import org.opensearch.OpenSearchParseException; import org.opensearch.Version; @@ -14,6 +14,7 @@ import org.opensearch.cluster.NamedDiff; import org.opensearch.cluster.decommission.DecommissionAttribute; import org.opensearch.cluster.decommission.DecommissionStatus; +import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.metadata.Metadata.Custom; import org.opensearch.common.Strings; import org.opensearch.common.io.stream.StreamInput; @@ -162,7 +163,7 @@ public static DecommissionAttributeMetadata fromXContent(XContentParser parser) ); } decommissionAttribute = new DecommissionAttribute(fieldName, value); - token = parser.nextToken(); + parser.nextToken(); } else { throw new OpenSearchParseException("failed to parse attribute type [{}], unexpected type", attributeType); } diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java index d699fc51e819a..4f81ddf1e32ff 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java @@ -26,7 +26,6 @@ import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; import org.opensearch.cluster.coordination.NodeRemovalClusterStateTaskExecutor; -import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.routing.allocation.AllocationService; diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 16c77a758a433..70a3b18519e89 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -17,7 +17,6 @@ import org.opensearch.cluster.NotClusterManagerException; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; import org.opensearch.cluster.coordination.CoordinationMetadata; -import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.routing.allocation.AllocationService; diff --git a/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java b/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java index a3c3b9a9b9c7b..9e2d0cc3a7fc4 100644 --- a/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java +++ b/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java @@ -37,10 +37,9 @@ import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateTaskExecutor; import org.opensearch.cluster.decommission.DecommissionAttribute; -import org.opensearch.cluster.decommission.DecommissionFailedException; +import org.opensearch.cluster.decommission.DecommissionAttributeMetadata; import org.opensearch.cluster.decommission.DecommissionStatus; import org.opensearch.cluster.decommission.NodeDecommissionedException; -import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; @@ -58,7 +57,6 @@ import java.util.HashSet; import java.util.Map; -import static java.util.Collections.emptyMap; import static org.hamcrest.Matchers.is; import static org.opensearch.test.VersionUtils.allVersions; import static org.opensearch.test.VersionUtils.maxCompatibleVersion; @@ -231,7 +229,7 @@ public void testJoinClusterWithNoDecommission() { Metadata.Builder metaBuilder = Metadata.builder(); Metadata metadata = metaBuilder.build(); DiscoveryNode discoveryNode = newDiscoveryNode(Collections.singletonMap("zone", "zone-2")); - JoinTaskExecutor.ensureNodeNotDecommissioned(discoveryNode, metadata); + JoinTaskExecutor.ensureNodeCommissioned(discoveryNode, metadata); } public void testPreventJoinClusterWithDecommission() { @@ -252,7 +250,7 @@ public void testPreventJoinClusterWithDecommission() { DiscoveryNode discoveryNode = newDiscoveryNode(Collections.singletonMap("zone", "zone-1")); expectThrows( NodeDecommissionedException.class, - () -> JoinTaskExecutor.ensureNodeNotDecommissioned(discoveryNode, metadata) + () -> JoinTaskExecutor.ensureNodeCommissioned(discoveryNode, metadata) ); } @@ -269,7 +267,7 @@ public void testJoinClusterWithDifferentDecommission() { Metadata metadata = metaBuilder.build(); DiscoveryNode discoveryNode = newDiscoveryNode(Collections.singletonMap("zone", "zone-2")); - JoinTaskExecutor.ensureNodeNotDecommissioned(discoveryNode, metadata); + JoinTaskExecutor.ensureNodeCommissioned(discoveryNode, metadata); } public void testJoinClusterWithDecommissionFailedOrInitOrRecommission() { @@ -289,7 +287,7 @@ public void testJoinClusterWithDecommissionFailedOrInitOrRecommission() { Metadata metadata = metaBuilder.build(); DiscoveryNode discoveryNode = newDiscoveryNode(Collections.singletonMap("zone", "zone-1")); - JoinTaskExecutor.ensureNodeNotDecommissioned(discoveryNode, metadata); + JoinTaskExecutor.ensureNodeCommissioned(discoveryNode, metadata); } private DiscoveryNode newDiscoveryNode(Map attributes) { diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java index 0f7f76fc5eeec..ee97f1768cd94 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java @@ -26,7 +26,6 @@ import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; import org.opensearch.cluster.coordination.CoordinationMetadata; -import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java index 5f55387745ca0..d93cd5ea51978 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java @@ -19,7 +19,6 @@ import org.opensearch.cluster.ClusterStateObserver; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; import org.opensearch.cluster.coordination.CoordinationMetadata; -import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.node.DiscoveryNodeRole; @@ -160,7 +159,7 @@ public void testDecommissioningNotInitiatedWhenAlreadyDecommissioned() { Metadata.builder( clusterService.state().metadata()).putCustom(DecommissionAttributeMetadata.TYPE, oldMetadata).build() )); - ActionListener listener = mock(ActionListener.class); + ActionListener listener = mock(ActionListener.class); DecommissionFailedException e = expectThrows(DecommissionFailedException.class, () -> { decommissionService.initiateAttributeDecommissioning( new DecommissionAttribute("zone", "zone_2"), listener, clusterService.state()); diff --git a/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataSerializationTests.java b/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataSerializationTests.java index d81c05b8e8da0..5423c2ed672a3 100644 --- a/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataSerializationTests.java +++ b/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataSerializationTests.java @@ -11,6 +11,7 @@ import org.opensearch.cluster.ClusterModule; import org.opensearch.cluster.Diff; import org.opensearch.cluster.decommission.DecommissionAttribute; +import org.opensearch.cluster.decommission.DecommissionAttributeMetadata; import org.opensearch.cluster.decommission.DecommissionStatus; import org.opensearch.common.io.stream.NamedWriteableRegistry; import org.opensearch.common.io.stream.Writeable; diff --git a/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataTests.java b/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataTests.java index bff57daef6109..746d4565b0db3 100644 --- a/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataTests.java +++ b/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataTests.java @@ -9,6 +9,7 @@ package org.opensearch.cluster.metadata; import org.opensearch.cluster.decommission.DecommissionAttribute; +import org.opensearch.cluster.decommission.DecommissionAttributeMetadata; import org.opensearch.cluster.decommission.DecommissionStatus; import org.opensearch.common.io.stream.NamedWriteableRegistry; import org.opensearch.test.AbstractNamedWriteableTestCase; diff --git a/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataXContentTests.java b/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataXContentTests.java index c632839acd4ca..030946f4510a1 100644 --- a/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataXContentTests.java +++ b/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataXContentTests.java @@ -9,6 +9,7 @@ package org.opensearch.cluster.metadata; import org.opensearch.cluster.decommission.DecommissionAttribute; +import org.opensearch.cluster.decommission.DecommissionAttributeMetadata; import org.opensearch.cluster.decommission.DecommissionStatus; import org.opensearch.common.xcontent.XContentParser; import org.opensearch.test.AbstractXContentTestCase; From 3a7f451e8cd3091dfe3a246f12781bc2b87c82e6 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 30 Aug 2022 15:38:00 +0530 Subject: [PATCH 054/187] Update exception name Signed-off-by: Rishab Nahata --- .../java/org/opensearch/OpenSearchException.java | 5 +++-- .../cluster/decommission/DecommissionService.java | 12 +++--------- ...ion.java => DecommissioningFailedException.java} | 8 ++++---- .../org/opensearch/ExceptionSerializationTests.java | 4 ++-- .../decommission/DecommissionServiceTests.java | 13 +++---------- 5 files changed, 15 insertions(+), 27 deletions(-) rename server/src/main/java/org/opensearch/cluster/decommission/{DecommissionFailedException.java => DecommissioningFailedException.java} (78%) diff --git a/server/src/main/java/org/opensearch/OpenSearchException.java b/server/src/main/java/org/opensearch/OpenSearchException.java index 5ae83c9df70d3..7799aef7fab38 100644 --- a/server/src/main/java/org/opensearch/OpenSearchException.java +++ b/server/src/main/java/org/opensearch/OpenSearchException.java @@ -34,6 +34,7 @@ import org.opensearch.action.support.replication.ReplicationOperation; import org.opensearch.cluster.action.shard.ShardStateAction; +import org.opensearch.cluster.decommission.DecommissioningFailedException; import org.opensearch.common.CheckedFunction; import org.opensearch.common.Nullable; import org.opensearch.common.ParseField; @@ -1611,8 +1612,8 @@ private enum OpenSearchExceptionHandle { V_3_0_0 ), DECOMMISSION_FAILED_EXCEPTION( - org.opensearch.cluster.decommission.DecommissionFailedException.class, - org.opensearch.cluster.decommission.DecommissionFailedException::new, + org.opensearch.cluster.decommission.DecommissioningFailedException.class, + org.opensearch.cluster.decommission.DecommissioningFailedException::new, 163, V_2_3_0 ), diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 70a3b18519e89..eac713f4961b4 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -16,25 +16,19 @@ import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.NotClusterManagerException; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; -import org.opensearch.cluster.coordination.CoordinationMetadata; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.routing.allocation.AllocationService; -import org.opensearch.cluster.routing.allocation.decider.AwarenessAllocationDecider; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Priority; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; import org.opensearch.threadpool.ThreadPool; -import org.opensearch.transport.TransportException; -import org.opensearch.transport.TransportResponseHandler; import org.opensearch.transport.TransportService; import org.opensearch.cluster.coordination.CoordinationMetadata.VotingConfigExclusion; -import java.io.IOException; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -229,7 +223,7 @@ public ClusterState execute(ClusterState currentState) { @Override public void onFailure(String source, Exception e) { - if (e instanceof DecommissionFailedException) { + if (e instanceof DecommissioningFailedException) { logger.error(() -> new ParameterizedMessage("failed to decommission attribute [{}]", decommissionAttribute.toString()), e); listener.onFailure(e); } else if (e instanceof NotClusterManagerException) { @@ -391,7 +385,7 @@ else if (!forcedAwarenessAttributes.get(decommissionAttribute.attributeName()).c } if (msg != null) { - throw new DecommissionFailedException(decommissionAttribute, msg); + throw new DecommissioningFailedException(decommissionAttribute, msg); } } @@ -402,7 +396,7 @@ private static void ensureNoAwarenessAttributeDecommissioned( // If the previous decommission request failed, we will allow the request to pass this check if (decommissionAttributeMetadata != null && !decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_FAILED)) { - throw new DecommissionFailedException( + throw new DecommissioningFailedException( decommissionAttribute, "one awareness attribute already decommissioned, recommission before triggering another decommission" ); diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionFailedException.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissioningFailedException.java similarity index 78% rename from server/src/main/java/org/opensearch/cluster/decommission/DecommissionFailedException.java rename to server/src/main/java/org/opensearch/cluster/decommission/DecommissioningFailedException.java index 3ba121dd90cee..fe1b9368ac712 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionFailedException.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissioningFailedException.java @@ -20,20 +20,20 @@ * @opensearch.internal */ -public class DecommissionFailedException extends OpenSearchException { +public class DecommissioningFailedException extends OpenSearchException { private final DecommissionAttribute decommissionAttribute; - public DecommissionFailedException(DecommissionAttribute decommissionAttribute, String msg) { + public DecommissioningFailedException(DecommissionAttribute decommissionAttribute, String msg) { this(decommissionAttribute, msg, null); } - public DecommissionFailedException(DecommissionAttribute decommissionAttribute, String msg, Throwable cause) { + public DecommissioningFailedException(DecommissionAttribute decommissionAttribute, String msg, Throwable cause) { super("[" + (decommissionAttribute == null ? "_na" : decommissionAttribute.toString()) + "] " + msg, cause); this.decommissionAttribute = decommissionAttribute; } - public DecommissionFailedException(StreamInput in) throws IOException { + public DecommissioningFailedException(StreamInput in) throws IOException { super(in); decommissionAttribute = new DecommissionAttribute(in); } diff --git a/server/src/test/java/org/opensearch/ExceptionSerializationTests.java b/server/src/test/java/org/opensearch/ExceptionSerializationTests.java index 6516cc80c7929..ff2bb77531486 100644 --- a/server/src/test/java/org/opensearch/ExceptionSerializationTests.java +++ b/server/src/test/java/org/opensearch/ExceptionSerializationTests.java @@ -49,7 +49,7 @@ import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.coordination.CoordinationStateRejectedException; import org.opensearch.cluster.coordination.NoClusterManagerBlockService; -import org.opensearch.cluster.decommission.DecommissionFailedException; +import org.opensearch.cluster.decommission.DecommissioningFailedException; import org.opensearch.cluster.decommission.NodeDecommissionedException; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.routing.IllegalShardRoutingStateException; @@ -862,7 +862,7 @@ public void testIds() { ids.put(160, NoSeedNodeLeftException.class); ids.put(161, ReplicationFailedException.class); ids.put(162, PrimaryShardClosedException.class); - ids.put(163, DecommissionFailedException.class); + ids.put(163, DecommissioningFailedException.class); ids.put(164, NodeDecommissionedException.class); Map, Integer> reverse = new HashMap<>(); diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java index d93cd5ea51978..13d036b2952f7 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java @@ -13,10 +13,8 @@ import org.junit.Before; import org.opensearch.Version; import org.opensearch.action.ActionListener; -import org.opensearch.action.search.CreatePitController; import org.opensearch.cluster.ClusterName; import org.opensearch.cluster.ClusterState; -import org.opensearch.cluster.ClusterStateObserver; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; import org.opensearch.cluster.coordination.CoordinationMetadata; import org.opensearch.cluster.metadata.Metadata; @@ -25,8 +23,6 @@ import org.opensearch.cluster.node.DiscoveryNodes; import org.opensearch.cluster.routing.allocation.AllocationService; import org.opensearch.cluster.routing.allocation.decider.AwarenessAllocationDecider; -import org.opensearch.cluster.routing.allocation.decider.ClusterRebalanceAllocationDecider; -import org.opensearch.cluster.routing.allocation.decider.ThrottlingAllocationDecider; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Settings; @@ -44,12 +40,9 @@ import static java.util.Collections.emptySet; import static java.util.Collections.singletonMap; -import static org.mockito.ArgumentMatchers.startsWith; import static org.mockito.Mockito.mock; import static org.opensearch.cluster.ClusterState.builder; import static org.opensearch.cluster.OpenSearchAllocationTestCase.createAllocationService; -import static org.opensearch.cluster.coordination.NoClusterManagerBlockService.NO_CLUSTER_MANAGER_BLOCK_SETTING; -import static org.opensearch.cluster.routing.allocation.decider.AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING; import static org.opensearch.test.ClusterServiceUtils.createClusterService; import static org.opensearch.test.ClusterServiceUtils.setState; @@ -125,7 +118,7 @@ public void shutdownThreadPoolAndClusterService() { public void testDecommissioningNotInitiatedForInvalidAttributeName() { DecommissionAttribute decommissionAttribute = new DecommissionAttribute("rack", "rack-a"); ActionListener listener = mock(ActionListener.class); - DecommissionFailedException e = expectThrows(DecommissionFailedException.class, () -> { + DecommissioningFailedException e = expectThrows(DecommissioningFailedException.class, () -> { decommissionService.initiateAttributeDecommissioning( decommissionAttribute, listener, clusterService.state()); }); @@ -136,7 +129,7 @@ public void testDecommissioningNotInitiatedForInvalidAttributeName() { public void testDecommissioningNotInitiatedForInvalidAttributeValue() { DecommissionAttribute decommissionAttribute = new DecommissionAttribute("zone", "random"); ActionListener listener = mock(ActionListener.class); - DecommissionFailedException e = expectThrows(DecommissionFailedException.class, () -> { + DecommissioningFailedException e = expectThrows(DecommissioningFailedException.class, () -> { decommissionService.initiateAttributeDecommissioning( decommissionAttribute, listener, clusterService.state()); }); @@ -160,7 +153,7 @@ public void testDecommissioningNotInitiatedWhenAlreadyDecommissioned() { clusterService.state().metadata()).putCustom(DecommissionAttributeMetadata.TYPE, oldMetadata).build() )); ActionListener listener = mock(ActionListener.class); - DecommissionFailedException e = expectThrows(DecommissionFailedException.class, () -> { + DecommissioningFailedException e = expectThrows(DecommissioningFailedException.class, () -> { decommissionService.initiateAttributeDecommissioning( new DecommissionAttribute("zone", "zone_2"), listener, clusterService.state()); }); From 5af85aea977064f881e2fe4f08201dfa15951460 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 30 Aug 2022 15:46:13 +0530 Subject: [PATCH 055/187] Fix spotless and precommit checks Signed-off-by: Rishab Nahata --- .../coordination/JoinTaskExecutor.java | 7 +- .../DecommissionAttributeMetadata.java | 2 - .../decommission/DecommissionController.java | 20 +--- .../decommission/DecommissionService.java | 108 ++++++++++-------- .../coordination/JoinTaskExecutorTests.java | 5 +- .../DecommissionControllerTests.java | 92 ++++++--------- .../DecommissionServiceTests.java | 62 +++++----- ...onAttributeMetadataSerializationTests.java | 7 +- 8 files changed, 135 insertions(+), 168 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java index a104499109683..ebf37e21bbfd6 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java @@ -484,11 +484,8 @@ public static void ensureNodeCommissioned(DiscoveryNode node, Metadata metadata) if (decommissionAttribute != null && status != null) { // We will let the node join the cluster if the current status is not IN_PROGRESS or SUCCESSFUL if (node.getAttributes().get(decommissionAttribute.attributeName()).equals(decommissionAttribute.attributeValue()) - && ( - status.equals(DecommissionStatus.DECOMMISSION_IN_PROGRESS) - || status.equals(DecommissionStatus.DECOMMISSION_SUCCESSFUL - ) - )) { + && (status.equals(DecommissionStatus.DECOMMISSION_IN_PROGRESS) + || status.equals(DecommissionStatus.DECOMMISSION_SUCCESSFUL))) { throw new NodeDecommissionedException( "node [{}] has decommissioned attribute [{}].", node.toString(), diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttributeMetadata.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttributeMetadata.java index 72bdfbdca78d3..0924a181fb458 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttributeMetadata.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttributeMetadata.java @@ -12,8 +12,6 @@ import org.opensearch.Version; import org.opensearch.cluster.AbstractNamedDiffable; import org.opensearch.cluster.NamedDiff; -import org.opensearch.cluster.decommission.DecommissionAttribute; -import org.opensearch.cluster.decommission.DecommissionStatus; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.metadata.Metadata.Custom; import org.opensearch.common.Strings; diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java index 4f81ddf1e32ff..1799479efe4cc 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java @@ -24,7 +24,6 @@ import org.opensearch.cluster.ClusterStateTaskConfig; import org.opensearch.cluster.ClusterStateTaskListener; import org.opensearch.cluster.ClusterStateUpdateTask; -import org.opensearch.cluster.ack.ClusterStateUpdateResponse; import org.opensearch.cluster.coordination.NodeRemovalClusterStateTaskExecutor; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; @@ -34,7 +33,6 @@ import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.unit.TimeValue; import org.opensearch.threadpool.ThreadPool; -import org.opensearch.transport.Transport; import org.opensearch.transport.TransportException; import org.opensearch.transport.TransportResponseHandler; import org.opensearch.transport.TransportService; @@ -42,7 +40,6 @@ import java.io.IOException; import java.util.Iterator; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Predicate; @@ -78,7 +75,7 @@ public void excludeDecommissionedNodesFromVotingConfig(Set nodes, Action transportService.sendRequest( transportService.getLocalNode(), AddVotingConfigExclusionsAction.NAME, - new AddVotingConfigExclusionsRequest(nodes.stream().toArray(String[] :: new)), + new AddVotingConfigExclusionsRequest(nodes.stream().toArray(String[]::new)), new TransportResponseHandler() { @Override public void handleResponse(AddVotingConfigExclusionsResponse response) { @@ -166,12 +163,7 @@ public void handleNodesDecommissionRequest( return true; }; - final ClusterStateObserver observer = new ClusterStateObserver( - clusterService, - timeout, - logger, - threadPool.getThreadContext() - ); + final ClusterStateObserver observer = new ClusterStateObserver(clusterService, timeout, logger, threadPool.getThreadContext()); observer.waitForNextChange(new ClusterStateObserver.Listener() { @Override @@ -198,10 +190,7 @@ public void onTimeout(TimeValue timeout) { }, allDecommissionedNodesRemovedPredicate); } - public void updateMetadataWithDecommissionStatus( - DecommissionStatus decommissionStatus, - ActionListener listener - ) { + public void updateMetadataWithDecommissionStatus(DecommissionStatus decommissionStatus, ActionListener listener) { clusterService.submitStateUpdateTask(decommissionStatus.status(), new ClusterStateUpdateTask(Priority.URGENT) { @Override public ClusterState execute(ClusterState currentState) { @@ -222,7 +211,8 @@ public void onFailure(String source, Exception e) { @Override public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { - DecommissionAttributeMetadata decommissionAttributeMetadata = newState.metadata().custom(DecommissionAttributeMetadata.TYPE); + DecommissionAttributeMetadata decommissionAttributeMetadata = newState.metadata() + .custom(DecommissionAttributeMetadata.TYPE); assert decommissionAttributeMetadata.status().equals(decommissionStatus); listener.onResponse(null); } diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index eac713f4961b4..89ddabb9fa19e 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -80,17 +80,9 @@ public DecommissionService( this.clusterService = clusterService; this.transportService = transportService; this.threadPool = threadPool; - this.decommissionController = new DecommissionController( - clusterService, - transportService, - allocationService, - threadPool - ); + this.decommissionController = new DecommissionController(clusterService, transportService, allocationService, threadPool); this.awarenessAttributes = CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING.get(settings); - clusterSettings.addSettingsUpdateConsumer( - CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING, - this::setAwarenessAttributes - ); + clusterSettings.addSettingsUpdateConsumer(CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING, this::setAwarenessAttributes); setForcedAwarenessAttributes(CLUSTER_ROUTING_ALLOCATION_AWARENESS_FORCE_GROUP_SETTING.get(settings)); clusterSettings.addSettingsUpdateConsumer( @@ -120,7 +112,8 @@ public void initiateAttributeDecommissioning( final ActionListener listener, ClusterState state ) { - // validates if the correct awareness attributes and forced awareness attribute set to the cluster before initiating decommission action + // validates if the correct awareness attributes and forced awareness attribute set to the cluster before initiating decommission + // action validateAwarenessAttribute(decommissionAttribute, awarenessAttributes, forcedAwarenessAttributes); DecommissionAttributeMetadata decommissionAttributeMetadata = state.metadata().custom(DecommissionAttributeMetadata.TYPE); @@ -135,7 +128,7 @@ public void initiateAttributeDecommissioning( // explicitly throwing NotClusterManagerException as we can certainly say the local cluster manager node will // be abdicated and soon will no longer be cluster manager. - if(transportService.getLocalNode().isClusterManagerNode() + if (transportService.getLocalNode().isClusterManagerNode() && !nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute)) { registerDecommissionAttribute(decommissionAttribute, listener); } else { @@ -149,19 +142,26 @@ public void initiateAttributeDecommissioning( private void excludeDecommissionedClusterManagerNodesFromVotingConfig(DecommissionAttribute decommissionAttribute) { Set clusterManagerNodesToBeDecommissioned = nodesWithDecommissionAttribute( - clusterService.state(), decommissionAttribute, true + clusterService.state(), + decommissionAttribute, + true ); Set clusterManagerNodesNameToBeDecommissioned = clusterManagerNodesToBeDecommissioned.stream() .map(DiscoveryNode::getName) .collect(Collectors.toSet()); - Set currentVotingConfigExclusions = clusterService.getClusterApplierService().state().coordinationMetadata().getVotingConfigExclusions(); - Set excludedNodesName = currentVotingConfigExclusions.stream().map(VotingConfigExclusion::getNodeName).collect(Collectors.toSet()); + Set currentVotingConfigExclusions = clusterService.getClusterApplierService() + .state() + .coordinationMetadata() + .getVotingConfigExclusions(); + Set excludedNodesName = currentVotingConfigExclusions.stream() + .map(VotingConfigExclusion::getNodeName) + .collect(Collectors.toSet()); // check if the to-be-excluded nodes are excluded. If yes, we don't need to exclude them again if (clusterManagerNodesNameToBeDecommissioned.size() == 0 || (clusterManagerNodesNameToBeDecommissioned.size() == excludedNodesName.size() - && excludedNodesName.containsAll(clusterManagerNodesNameToBeDecommissioned))) { + && excludedNodesName.containsAll(clusterManagerNodesNameToBeDecommissioned))) { return; } // send a transport request to exclude to-be-decommissioned cluster manager eligible nodes from voting config @@ -171,8 +171,8 @@ private void excludeDecommissionedClusterManagerNodesFromVotingConfig(Decommissi @Override public void onResponse(Void unused) { logger.info( - "successfully removed decommissioned cluster manager eligible nodes [{}] from voting config " - , clusterManagerNodesToBeDecommissioned.toString() + "successfully removed decommissioned cluster manager eligible nodes [{}] from voting config ", + clusterManagerNodesToBeDecommissioned.toString() ); } @@ -224,20 +224,27 @@ public ClusterState execute(ClusterState currentState) { @Override public void onFailure(String source, Exception e) { if (e instanceof DecommissioningFailedException) { - logger.error(() -> new ParameterizedMessage("failed to decommission attribute [{}]", decommissionAttribute.toString()), e); + logger.error( + () -> new ParameterizedMessage("failed to decommission attribute [{}]", decommissionAttribute.toString()), + e + ); listener.onFailure(e); } else if (e instanceof NotClusterManagerException) { logger.debug( () -> new ParameterizedMessage( "cluster-manager updated while executing request for decommission attribute [{}]", decommissionAttribute.toString() - ), e + ), + e ); // we don't want to send the failure response to the listener here as the request will be retried } else { - logger.error(() -> new ParameterizedMessage( - "failed to initiate decommissioning for attribute [{}]", decommissionAttribute.toString() - ), e + logger.error( + () -> new ParameterizedMessage( + "failed to initiate decommissioning for attribute [{}]", + decommissionAttribute.toString() + ), + e ); listener.onFailure(e); } @@ -272,10 +279,12 @@ public void onResponse(Void unused) { @Override public void onFailure(Exception e) { - logger.error(() -> new ParameterizedMessage( + logger.error( + () -> new ParameterizedMessage( "failed to update decommission status to [{}], will not proceed with decommission", DecommissionStatus.DECOMMISSION_IN_PROGRESS - ), e + ), + e ); } } @@ -311,8 +320,10 @@ private void clearVotingConfigExclusionAndUpdateStatus(boolean decommissionSucce ActionListener statusUpdateListener = new ActionListener() { @Override public void onResponse(Void unused) { - logger.info("successful updated decommission status with [{}]", - decommissionSuccessful ? DecommissionStatus.DECOMMISSION_SUCCESSFUL : DecommissionStatus.DECOMMISSION_FAILED); + logger.info( + "successful updated decommission status with [{}]", + decommissionSuccessful ? DecommissionStatus.DECOMMISSION_SUCCESSFUL : DecommissionStatus.DECOMMISSION_FAILED + ); } @Override @@ -320,20 +331,20 @@ public void onFailure(Exception e) { logger.error("failed to update the decommission status"); } }; - decommissionController.clearVotingConfigExclusion( - new ActionListener() { - @Override - public void onResponse(Void unused) { - DecommissionStatus updateStatusWith = decommissionSuccessful? DecommissionStatus.DECOMMISSION_SUCCESSFUL : DecommissionStatus.DECOMMISSION_FAILED; - decommissionController.updateMetadataWithDecommissionStatus(updateStatusWith, statusUpdateListener); - } + decommissionController.clearVotingConfigExclusion(new ActionListener() { + @Override + public void onResponse(Void unused) { + DecommissionStatus updateStatusWith = decommissionSuccessful + ? DecommissionStatus.DECOMMISSION_SUCCESSFUL + : DecommissionStatus.DECOMMISSION_FAILED; + decommissionController.updateMetadataWithDecommissionStatus(updateStatusWith, statusUpdateListener); + } - @Override - public void onFailure(Exception e) { - decommissionController.updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_FAILED, statusUpdateListener); - } + @Override + public void onFailure(Exception e) { + decommissionController.updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_FAILED, statusUpdateListener); } - ); + }); } public Set nodesWithDecommissionAttribute( @@ -346,8 +357,9 @@ public Set nodesWithDecommissionAttribute( discoveryNode, decommissionAttribute ); - Iterator nodesIter = onlyClusterManagerNodes? clusterState.nodes().getClusterManagerNodes().valuesIt() : - clusterState.nodes().getNodes().valuesIt(); + Iterator nodesIter = onlyClusterManagerNodes + ? clusterState.nodes().getClusterManagerNodes().valuesIt() + : clusterState.nodes().getNodes().valuesIt(); while (nodesIter.hasNext()) { final DiscoveryNode node = nodesIter.next(); @@ -369,18 +381,14 @@ private static void validateAwarenessAttribute( ) { String msg = null; if (awarenessAttributes == null) { - msg = "awareness attribute not set to the cluster."; - } - else if (forcedAwarenessAttributes == null) { + msg = "awareness attribute not set to the cluster."; + } else if (forcedAwarenessAttributes == null) { msg = "forced awareness attribute not set to the cluster."; - } - else if (!awarenessAttributes.contains(decommissionAttribute.attributeName())) { + } else if (!awarenessAttributes.contains(decommissionAttribute.attributeName())) { msg = "invalid awareness attribute requested for decommissioning"; - } - else if (!forcedAwarenessAttributes.containsKey(decommissionAttribute.attributeName())) { + } else if (!forcedAwarenessAttributes.containsKey(decommissionAttribute.attributeName())) { msg = "forced awareness attribute [" + forcedAwarenessAttributes.toString() + "] doesn't have the decommissioning attribute"; - } - else if (!forcedAwarenessAttributes.get(decommissionAttribute.attributeName()).contains(decommissionAttribute.attributeValue()) ) { + } else if (!forcedAwarenessAttributes.get(decommissionAttribute.attributeName()).contains(decommissionAttribute.attributeValue())) { msg = "invalid awareness attribute value requested for decommissioning. Set forced awareness values before to decommission"; } diff --git a/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java b/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java index 9e2d0cc3a7fc4..734f112bdce3d 100644 --- a/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java +++ b/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java @@ -248,10 +248,7 @@ public void testPreventJoinClusterWithDecommission() { Metadata metadata = metaBuilder.build(); DiscoveryNode discoveryNode = newDiscoveryNode(Collections.singletonMap("zone", "zone-1")); - expectThrows( - NodeDecommissionedException.class, - () -> JoinTaskExecutor.ensureNodeCommissioned(discoveryNode, metadata) - ); + expectThrows(NodeDecommissionedException.class, () -> JoinTaskExecutor.ensureNodeCommissioned(discoveryNode, metadata)); } public void testJoinClusterWithDifferentDecommission() { diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java index ee97f1768cd94..e5d7ec60c0e23 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java @@ -9,22 +9,15 @@ package org.opensearch.cluster.decommission; import org.junit.After; -import org.junit.AfterClass; import org.junit.Before; -import org.junit.BeforeClass; import org.opensearch.OpenSearchTimeoutException; import org.opensearch.Version; import org.opensearch.action.ActionListener; import org.opensearch.action.admin.cluster.configuration.TransportAddVotingConfigExclusionsAction; -import org.opensearch.action.admin.cluster.configuration.TransportAddVotingConfigExclusionsActionTests; import org.opensearch.action.admin.cluster.configuration.TransportClearVotingConfigExclusionsAction; import org.opensearch.action.support.ActionFilters; import org.opensearch.cluster.ClusterName; import org.opensearch.cluster.ClusterState; -import org.opensearch.cluster.coordination.CoordinationMetadata.VotingConfigExclusion; -import org.opensearch.cluster.ClusterStateObserver; -import org.opensearch.cluster.ClusterStateUpdateTask; -import org.opensearch.cluster.ack.ClusterStateUpdateResponse; import org.opensearch.cluster.coordination.CoordinationMetadata; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.metadata.Metadata; @@ -33,7 +26,6 @@ import org.opensearch.cluster.node.DiscoveryNodes; import org.opensearch.cluster.routing.allocation.AllocationService; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.collect.ImmutableOpenMap; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; @@ -44,11 +36,9 @@ import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; @@ -56,21 +46,11 @@ import java.util.stream.Collectors; import java.util.stream.StreamSupport; -import static java.util.Collections.emptyMap; import static java.util.Collections.emptySet; import static java.util.Collections.singletonMap; -import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.sameInstance; import static org.hamcrest.Matchers.startsWith; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.opensearch.action.admin.cluster.configuration.TransportAddVotingConfigExclusionsAction.MAXIMUM_VOTING_CONFIG_EXCLUSIONS_SETTING; import static org.opensearch.cluster.ClusterState.builder; import static org.opensearch.cluster.OpenSearchAllocationTestCase.createAllocationService; import static org.opensearch.test.ClusterServiceUtils.createClusterService; @@ -105,10 +85,7 @@ public void setTransportServiceAndDefaultClusterState() { clusterState = setLocalNodeAsClusterManagerNode(clusterState, "node1"); clusterState = setThreeNodesInVotingConfig(clusterState); final ClusterState.Builder builder = builder(clusterState); - setState( - clusterService, - builder - ); + setState(clusterService, builder); final MockTransport transport = new MockTransport(); transportService = transport.createTransportService( Settings.EMPTY, @@ -155,20 +132,17 @@ public void shutdownThreadPoolAndClusterService() { public void testAddNodesToVotingConfigExclusion() throws InterruptedException { final CountDownLatch countDownLatch = new CountDownLatch(1); Set nodesToRemoveFromVotingConfig = Collections.singleton(randomFrom("node1", "node6", "node11")); - decommissionController.excludeDecommissionedNodesFromVotingConfig( - nodesToRemoveFromVotingConfig, - new ActionListener() { - @Override - public void onResponse(Void unused) { - countDownLatch.countDown(); - } + decommissionController.excludeDecommissionedNodesFromVotingConfig(nodesToRemoveFromVotingConfig, new ActionListener() { + @Override + public void onResponse(Void unused) { + countDownLatch.countDown(); + } - @Override - public void onFailure(Exception e) { - fail("unexpected failure occurred while removing node from voting config " + e); - } + @Override + public void onFailure(Exception e) { + fail("unexpected failure occurred while removing node from voting config " + e); } - ); + }); assertTrue(countDownLatch.await(30, TimeUnit.SECONDS)); clusterService.getClusterApplierService().state().getVotingConfigExclusions().forEach(vce -> { assertTrue(nodesToRemoveFromVotingConfig.contains(vce.getNodeName())); @@ -186,14 +160,13 @@ public void onResponse(Void unused) { @Override public void onFailure(Exception e) { - fail("unexpected failure occurred while clearing voting config exclusion" + e); + fail("unexpected failure occurred while clearing voting config exclusion" + e); } }); assertTrue(countDownLatch.await(30, TimeUnit.SECONDS)); assertThat(clusterService.getClusterApplierService().state().getVotingConfigExclusions(), empty()); } - public void testNodesRemovedForDecommissionRequestSuccessfulResponse() throws InterruptedException { final CountDownLatch countDownLatch = new CountDownLatch(1); Set nodesToBeRemoved = new HashSet<>(); @@ -222,9 +195,8 @@ public void onFailure(Exception e) { assertTrue(countDownLatch.await(30, TimeUnit.SECONDS)); // test all 5 nodes removed and cluster has 10 nodes - Set nodes = StreamSupport.stream( - clusterService.getClusterApplierService().state().nodes().spliterator(), false - ).collect(Collectors.toSet()); + Set nodes = StreamSupport.stream(clusterService.getClusterApplierService().state().nodes().spliterator(), false) + .collect(Collectors.toSet()); assertEquals(nodes.size(), 10); // test no nodes part of zone-3 for (DiscoveryNode node : nodes) { @@ -274,20 +246,17 @@ public void testSuccessfulDecommissionStatusMetadataUpdate() throws InterruptedE state = ClusterState.builder(state).metadata(mdBuilder).build(); setState(clusterService, state); - decommissionController.updateMetadataWithDecommissionStatus( - DecommissionStatus.DECOMMISSION_SUCCESSFUL, - new ActionListener() { - @Override - public void onResponse(Void unused) { - countDownLatch.countDown(); - } + decommissionController.updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_SUCCESSFUL, new ActionListener() { + @Override + public void onResponse(Void unused) { + countDownLatch.countDown(); + } - @Override - public void onFailure(Exception e) { - fail("decommission status update failed"); - } + @Override + public void onFailure(Exception e) { + fail("decommission status update failed"); } - ); + }); assertTrue(countDownLatch.await(30, TimeUnit.SECONDS)); ClusterState newState = clusterService.getClusterApplierService().state(); DecommissionAttributeMetadata decommissionAttributeMetadata = newState.metadata().custom(DecommissionAttributeMetadata.TYPE); @@ -311,15 +280,18 @@ private ClusterState setLocalNodeAsClusterManagerNode(ClusterState clusterState, private ClusterState setThreeNodesInVotingConfig(ClusterState clusterState) { final CoordinationMetadata.VotingConfiguration votingConfiguration = CoordinationMetadata.VotingConfiguration.of( - clusterState.nodes().get("node1"), clusterState.nodes().get("node6"), clusterState.nodes().get("node11") + clusterState.nodes().get("node1"), + clusterState.nodes().get("node6"), + clusterState.nodes().get("node11") ); - Metadata.Builder builder = Metadata.builder().coordinationMetadata( - CoordinationMetadata.builder() - .lastAcceptedConfiguration(votingConfiguration) - .lastCommittedConfiguration(votingConfiguration) - .build() - ); + Metadata.Builder builder = Metadata.builder() + .coordinationMetadata( + CoordinationMetadata.builder() + .lastAcceptedConfiguration(votingConfiguration) + .lastCommittedConfiguration(votingConfiguration) + .build() + ); clusterState = ClusterState.builder(clusterState).metadata(builder).build(); return clusterState; } diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java index 13d036b2952f7..04bb876761113 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java @@ -76,10 +76,7 @@ public void setUpService() { clusterState = setLocalNodeAsClusterManagerNode(clusterState, "node1"); clusterState = setThreeNodesInVotingConfig(clusterState); final ClusterState.Builder builder = builder(clusterState); - setState( - clusterService, - builder - ); + setState(clusterService, builder); final MockTransport transport = new MockTransport(); transportService = transport.createTransportService( Settings.EMPTY, @@ -118,10 +115,10 @@ public void shutdownThreadPoolAndClusterService() { public void testDecommissioningNotInitiatedForInvalidAttributeName() { DecommissionAttribute decommissionAttribute = new DecommissionAttribute("rack", "rack-a"); ActionListener listener = mock(ActionListener.class); - DecommissioningFailedException e = expectThrows(DecommissioningFailedException.class, () -> { - decommissionService.initiateAttributeDecommissioning( - decommissionAttribute, listener, clusterService.state()); - }); + DecommissioningFailedException e = expectThrows( + DecommissioningFailedException.class, + () -> { decommissionService.initiateAttributeDecommissioning(decommissionAttribute, listener, clusterService.state()); } + ); assertThat(e.getMessage(), Matchers.endsWith("invalid awareness attribute requested for decommissioning")); } @@ -129,13 +126,15 @@ public void testDecommissioningNotInitiatedForInvalidAttributeName() { public void testDecommissioningNotInitiatedForInvalidAttributeValue() { DecommissionAttribute decommissionAttribute = new DecommissionAttribute("zone", "random"); ActionListener listener = mock(ActionListener.class); - DecommissioningFailedException e = expectThrows(DecommissioningFailedException.class, () -> { - decommissionService.initiateAttributeDecommissioning( - decommissionAttribute, listener, clusterService.state()); - }); + DecommissioningFailedException e = expectThrows( + DecommissioningFailedException.class, + () -> { decommissionService.initiateAttributeDecommissioning(decommissionAttribute, listener, clusterService.state()); } + ); assertThat( e.getMessage(), - Matchers.endsWith("invalid awareness attribute value requested for decommissioning. Set forced awareness values before to decommission") + Matchers.endsWith( + "invalid awareness attribute value requested for decommissioning. Set forced awareness values before to decommission" + ) ); } @@ -149,14 +148,20 @@ public void testDecommissioningNotInitiatedWhenAlreadyDecommissioned() { setState( clusterService, builder.metadata( - Metadata.builder( - clusterService.state().metadata()).putCustom(DecommissionAttributeMetadata.TYPE, oldMetadata).build() - )); + Metadata.builder(clusterService.state().metadata()).putCustom(DecommissionAttributeMetadata.TYPE, oldMetadata).build() + ) + ); ActionListener listener = mock(ActionListener.class); - DecommissioningFailedException e = expectThrows(DecommissioningFailedException.class, () -> { - decommissionService.initiateAttributeDecommissioning( - new DecommissionAttribute("zone", "zone_2"), listener, clusterService.state()); - }); + DecommissioningFailedException e = expectThrows( + DecommissioningFailedException.class, + () -> { + decommissionService.initiateAttributeDecommissioning( + new DecommissionAttribute("zone", "zone_2"), + listener, + clusterService.state() + ); + } + ); assertThat( e.getMessage(), Matchers.endsWith("one awareness attribute already decommissioned, recommission before triggering another decommission") @@ -180,15 +185,18 @@ private ClusterState setLocalNodeAsClusterManagerNode(ClusterState clusterState, private ClusterState setThreeNodesInVotingConfig(ClusterState clusterState) { final CoordinationMetadata.VotingConfiguration votingConfiguration = CoordinationMetadata.VotingConfiguration.of( - clusterState.nodes().get("node1"), clusterState.nodes().get("node6"), clusterState.nodes().get("node11") + clusterState.nodes().get("node1"), + clusterState.nodes().get("node6"), + clusterState.nodes().get("node11") ); - Metadata.Builder builder = Metadata.builder().coordinationMetadata( - CoordinationMetadata.builder() - .lastAcceptedConfiguration(votingConfiguration) - .lastCommittedConfiguration(votingConfiguration) - .build() - ); + Metadata.Builder builder = Metadata.builder() + .coordinationMetadata( + CoordinationMetadata.builder() + .lastAcceptedConfiguration(votingConfiguration) + .lastCommittedConfiguration(votingConfiguration) + .build() + ); clusterState = ClusterState.builder(clusterState).metadata(builder).build(); return clusterState; } diff --git a/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataSerializationTests.java b/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataSerializationTests.java index 5423c2ed672a3..60b3a03848830 100644 --- a/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataSerializationTests.java +++ b/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataSerializationTests.java @@ -54,13 +54,10 @@ protected Metadata.Custom makeTestChanges(Metadata.Custom testInstance) { if (randomBoolean()) { attributeName = randomAlphaOfLength(6); } - if(randomBoolean()) { + if (randomBoolean()) { attributeValue = randomAlphaOfLength(6); } - return new DecommissionAttributeMetadata( - new DecommissionAttribute(attributeName, attributeValue), - decommissionStatus - ); + return new DecommissionAttributeMetadata(new DecommissionAttribute(attributeName, attributeValue), decommissionStatus); } @Override From 7a43c83af0945015d7af8fc709c944492a80280f Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 30 Aug 2022 15:56:26 +0530 Subject: [PATCH 056/187] Update enum Signed-off-by: Rishab Nahata --- server/src/main/java/org/opensearch/OpenSearchException.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/org/opensearch/OpenSearchException.java b/server/src/main/java/org/opensearch/OpenSearchException.java index 7799aef7fab38..83a11ba10da56 100644 --- a/server/src/main/java/org/opensearch/OpenSearchException.java +++ b/server/src/main/java/org/opensearch/OpenSearchException.java @@ -1611,7 +1611,7 @@ private enum OpenSearchExceptionHandle { 162, V_3_0_0 ), - DECOMMISSION_FAILED_EXCEPTION( + DECOMMISSIONING_FAILED_EXCEPTION( org.opensearch.cluster.decommission.DecommissioningFailedException.class, org.opensearch.cluster.decommission.DecommissioningFailedException::new, 163, From 3f038e29b28112df53d0e9d668b3d0ab5e7f2b75 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 30 Aug 2022 16:11:01 +0530 Subject: [PATCH 057/187] Fix spotless and precommit checks Signed-off-by: Rishab Nahata --- server/src/main/java/org/opensearch/OpenSearchException.java | 1 - 1 file changed, 1 deletion(-) diff --git a/server/src/main/java/org/opensearch/OpenSearchException.java b/server/src/main/java/org/opensearch/OpenSearchException.java index 83a11ba10da56..932aae741160e 100644 --- a/server/src/main/java/org/opensearch/OpenSearchException.java +++ b/server/src/main/java/org/opensearch/OpenSearchException.java @@ -34,7 +34,6 @@ import org.opensearch.action.support.replication.ReplicationOperation; import org.opensearch.cluster.action.shard.ShardStateAction; -import org.opensearch.cluster.decommission.DecommissioningFailedException; import org.opensearch.common.CheckedFunction; import org.opensearch.common.Nullable; import org.opensearch.common.ParseField; From 6f0d8a6f36818d0ff85ebb3e8bc1a80c512efa41 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 30 Aug 2022 20:07:39 +0530 Subject: [PATCH 058/187] Add package-info and Changelog Signed-off-by: Rishab Nahata --- CHANGELOG.md | 1 + .../cluster/decommission/package-info.java | 12 ++++++++++++ 2 files changed, 13 insertions(+) create mode 100644 server/src/main/java/org/opensearch/cluster/decommission/package-info.java diff --git a/CHANGELOG.md b/CHANGELOG.md index b3c5d731af082..445bac64b9371 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Dependency updates (httpcore, mockito, slf4j, httpasyncclient, commons-codec) ([#4308](https://github.com/opensearch-project/OpenSearch/pull/4308)) - Use RemoteSegmentStoreDirectory instead of RemoteDirectory ([#4240](https://github.com/opensearch-project/OpenSearch/pull/4240)) - Plugin ZIP publication groupId value is configurable ([#4156](https://github.com/opensearch-project/OpenSearch/pull/4156)) +- Add DecommissionService and helper to execute awareness attribute decommissioning ([#4084](https://github.com/opensearch-project/OpenSearch/pull/4084)) ### Deprecated diff --git a/server/src/main/java/org/opensearch/cluster/decommission/package-info.java b/server/src/main/java/org/opensearch/cluster/decommission/package-info.java new file mode 100644 index 0000000000000..256c2f22253cc --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/decommission/package-info.java @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * Decommission lifecycle classes + */ +package org.opensearch.cluster.decommission; From beb09af65710aa17909f977a61b7a5414f7967cc Mon Sep 17 00:00:00 2001 From: Kartik Ganesh Date: Tue, 30 Aug 2022 11:17:11 -0700 Subject: [PATCH 059/187] Adding @dreamer-89 to Opensearch maintainers. (#4342) Signed-off-by: Kartik Ganesh Signed-off-by: Kartik Ganesh --- CHANGELOG.md | 1 + MAINTAINERS.md | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b3c5d731af082..c7f17dac5bf13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ## [Unreleased] ### Added - Github workflow for changelog verification ([#4085](https://github.com/opensearch-project/OpenSearch/pull/4085)) +- Added @dreamer-89 as an Opensearch maintainer ([#4342](https://github.com/opensearch-project/OpenSearch/pull/4342)) ### Changed - Dependency updates (httpcore, mockito, slf4j, httpasyncclient, commons-codec) ([#4308](https://github.com/opensearch-project/OpenSearch/pull/4308)) diff --git a/MAINTAINERS.md b/MAINTAINERS.md index 94e649a634c7f..2f54656b2ab59 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -23,6 +23,7 @@ | Ryan Bogan | [ryanbogan](https://github.com/ryanbogan) | Amazon | | Sarat Vemulapalli | [saratvemulapalli](https://github.com/saratvemulapalli) | Amazon | | Shweta Thareja |[shwetathareja](https://github.com/shwetathareja) | Amazon | +| Suraj Singh |[dreamer-89](https://github.com/dreamer-89) | Amazon | | Tianli Feng | [tlfeng](https://github.com/tlfeng) | Amazon | | Vacha Shah | [VachaShah](https://github.com/VachaShah) | Amazon | | Xue Zhou | [xuezhou25](https://github.com/xuezhou25) | Amazon | From 4bccdbe9bdf474d79d3ff9e68e53267174b001ac Mon Sep 17 00:00:00 2001 From: Rabi Panda Date: Tue, 30 Aug 2022 13:21:15 -0700 Subject: [PATCH 060/187] [CVE] Update snakeyaml dependency (#4341) The package `org.yaml:snakeyaml` before version 1.31 are vulnerable to Denial of Service (DoS) due missing to nested depth limitation for collections. Details at https://nvd.nist.gov/vuln/detail/CVE-2022-25857 Signed-off-by: Rabi Panda --- CHANGELOG.md | 1 + buildSrc/version.properties | 2 +- libs/x-content/licenses/snakeyaml-1.26.jar.sha1 | 1 - libs/x-content/licenses/snakeyaml-1.31.jar.sha1 | 1 + 4 files changed, 3 insertions(+), 2 deletions(-) delete mode 100644 libs/x-content/licenses/snakeyaml-1.26.jar.sha1 create mode 100644 libs/x-content/licenses/snakeyaml-1.31.jar.sha1 diff --git a/CHANGELOG.md b/CHANGELOG.md index c7f17dac5bf13..877e472ac66d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Add timeout on Mockito.verify to reduce flakyness in testReplicationOnDone test([#4314](https://github.com/opensearch-project/OpenSearch/pull/4314)) ### Security +- CVE-2022-25857 org.yaml:snakeyaml DOS vulnerability ([#4341](https://github.com/opensearch-project/OpenSearch/pull/4341)) ## [2.x] ### Added diff --git a/buildSrc/version.properties b/buildSrc/version.properties index 876910d5351d0..072dcc4578977 100644 --- a/buildSrc/version.properties +++ b/buildSrc/version.properties @@ -11,7 +11,7 @@ spatial4j = 0.7 jts = 1.15.0 jackson = 2.13.3 jackson_databind = 2.13.3 -snakeyaml = 1.26 +snakeyaml = 1.31 icu4j = 70.1 supercsv = 2.4.0 log4j = 2.17.1 diff --git a/libs/x-content/licenses/snakeyaml-1.26.jar.sha1 b/libs/x-content/licenses/snakeyaml-1.26.jar.sha1 deleted file mode 100644 index fde3aba8edad0..0000000000000 --- a/libs/x-content/licenses/snakeyaml-1.26.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -a78a8747147d2c5807683e76ec2b633e95c14fe9 \ No newline at end of file diff --git a/libs/x-content/licenses/snakeyaml-1.31.jar.sha1 b/libs/x-content/licenses/snakeyaml-1.31.jar.sha1 new file mode 100644 index 0000000000000..1ac9b78b88687 --- /dev/null +++ b/libs/x-content/licenses/snakeyaml-1.31.jar.sha1 @@ -0,0 +1 @@ +cf26b7b05fef01e7bec00cb88ab4feeeba743e12 \ No newline at end of file From 82bda895ccf039c48ea73c68c845908cd6289381 Mon Sep 17 00:00:00 2001 From: Kunal Kotwani Date: Tue, 30 Aug 2022 15:33:07 -0700 Subject: [PATCH 061/187] Fixed commit workflow for dependabot PR helper (#4331) * Fixed label for dependabot PR helper Signed-off-by: Kunal Kotwani * Update autocommit workflow for dependabot changelog Signed-off-by: Kunal Kotwani * Add version config for dependabot changelog helper Signed-off-by: Kunal Kotwani Signed-off-by: Kunal Kotwani --- .github/workflows/changelog_verifier.yml | 6 ++++++ CHANGELOG.md | 2 ++ 2 files changed, 8 insertions(+) diff --git a/.github/workflows/changelog_verifier.yml b/.github/workflows/changelog_verifier.yml index ee9bf5e18d0d5..ac0c0ec4d7297 100644 --- a/.github/workflows/changelog_verifier.yml +++ b/.github/workflows/changelog_verifier.yml @@ -14,9 +14,15 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} - uses: dangoslen/dependabot-changelog-helper@v1 + with: + version: 'Unreleased' - uses: stefanzweifel/git-auto-commit-action@v4 with: commit_message: "Update changelog" + branch: ${{ github.head_ref }} + commit_user_name: dependabot[bot] + commit_user_email: support@github.com + commit_options: '--signoff' - uses: dangoslen/changelog-enforcer@v3 diff --git a/CHANGELOG.md b/CHANGELOG.md index 877e472ac66d4..9efafb1e69a50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Restore using the class ClusterInfoRequest and ClusterInfoRequestBuilder from package 'org.opensearch.action.support.master.info' for subclasses ([#4307](https://github.com/opensearch-project/OpenSearch/pull/4307)) - Do not fail replica shard due to primary closure ([#4133](https://github.com/opensearch-project/OpenSearch/pull/4133)) - Add timeout on Mockito.verify to reduce flakyness in testReplicationOnDone test([#4314](https://github.com/opensearch-project/OpenSearch/pull/4314)) +- Commit workflow for dependabot changelog helper ([#4331](https://github.com/opensearch-project/OpenSearch/pull/4331)) ### Security - CVE-2022-25857 org.yaml:snakeyaml DOS vulnerability ([#4341](https://github.com/opensearch-project/OpenSearch/pull/4341)) @@ -38,6 +39,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### Fixed - PR reference to checkout code for changelog verifier ([#4296](https://github.com/opensearch-project/OpenSearch/pull/4296)) +- Commit workflow for dependabot changelog helper ([#4331](https://github.com/opensearch-project/OpenSearch/pull/4331)) ### Security From 48d68699299346873d12bcfaf1013c148a0fe166 Mon Sep 17 00:00:00 2001 From: Rabi Panda Date: Tue, 30 Aug 2022 15:41:59 -0700 Subject: [PATCH 062/187] Add release notes for patch release 1.3.5 (#4343) Signed-off-by: Rabi Panda --- CHANGELOG.md | 1 + release-notes/opensearch.release-notes-1.3.5.md | 9 +++++++++ 2 files changed, 10 insertions(+) create mode 100644 release-notes/opensearch.release-notes-1.3.5.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 9efafb1e69a50..8c000c02e86ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### Added - Github workflow for changelog verification ([#4085](https://github.com/opensearch-project/OpenSearch/pull/4085)) - Added @dreamer-89 as an Opensearch maintainer ([#4342](https://github.com/opensearch-project/OpenSearch/pull/4342)) +- Added release notes for 1.3.5 ([#4343](https://github.com/opensearch-project/OpenSearch/pull/4343)) ### Changed - Dependency updates (httpcore, mockito, slf4j, httpasyncclient, commons-codec) ([#4308](https://github.com/opensearch-project/OpenSearch/pull/4308)) diff --git a/release-notes/opensearch.release-notes-1.3.5.md b/release-notes/opensearch.release-notes-1.3.5.md new file mode 100644 index 0000000000000..fbf866bb6e112 --- /dev/null +++ b/release-notes/opensearch.release-notes-1.3.5.md @@ -0,0 +1,9 @@ +## 2022-08-30 Version 1.3.5 Release Notes + +### Upgrades +* OpenJDK Update (July 2022 Patch releases) ([#4097](https://github.com/opensearch-project/OpenSearch/pull/4097)) +* Update Netty to 4.1.79.Final ([#3868](https://github.com/opensearch-project/OpenSearch/pull/3868)) + +### Bug Fixes +* OpenSearch crashes on closed client connection before search reply when total ops higher compared to expected ([#4143](https://github.com/opensearch-project/OpenSearch/pull/4143)) +* gradle check failing with java heap OutOfMemoryError ([#4150](https://github.com/opensearch-project/OpenSearch/pull/4150)) From f16ea9c8ec0a181c77b57388b2f3a322bcb47814 Mon Sep 17 00:00:00 2001 From: Rabi Panda Date: Tue, 30 Aug 2022 16:25:39 -0700 Subject: [PATCH 063/187] Add release notes for patch release 2.2.1 (#4344) Signed-off-by: Rabi Panda --- CHANGELOG.md | 1 + release-notes/opensearch.release-notes-2.2.1.md | 7 +++++++ 2 files changed, 8 insertions(+) create mode 100644 release-notes/opensearch.release-notes-2.2.1.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c000c02e86ba..3b3d54a802e67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Github workflow for changelog verification ([#4085](https://github.com/opensearch-project/OpenSearch/pull/4085)) - Added @dreamer-89 as an Opensearch maintainer ([#4342](https://github.com/opensearch-project/OpenSearch/pull/4342)) - Added release notes for 1.3.5 ([#4343](https://github.com/opensearch-project/OpenSearch/pull/4343)) +- Added release notes for 2.2.1 ([#4344](https://github.com/opensearch-project/OpenSearch/pull/4344)) ### Changed - Dependency updates (httpcore, mockito, slf4j, httpasyncclient, commons-codec) ([#4308](https://github.com/opensearch-project/OpenSearch/pull/4308)) diff --git a/release-notes/opensearch.release-notes-2.2.1.md b/release-notes/opensearch.release-notes-2.2.1.md new file mode 100644 index 0000000000000..974ff8e09a426 --- /dev/null +++ b/release-notes/opensearch.release-notes-2.2.1.md @@ -0,0 +1,7 @@ +## 2022-08-30 Version 2.2.1 Release Notes + +### Upgrades +* Update Gradle to 7.5.1 ([#4211](https://github.com/opensearch-project/OpenSearch/pull/4211)) + +### Bug Fixes +* gradle check failing with java heap OutOfMemoryError ([#4150](https://github.com/opensearch-project/OpenSearch/pull/4150)) From 4f65ef58ef7e4836b93cbe12afcafdd07fea12a8 Mon Sep 17 00:00:00 2001 From: Kunal Kotwani Date: Tue, 30 Aug 2022 17:34:56 -0700 Subject: [PATCH 064/187] Add label configuration for dependabot PRs (#4348) --- .github/dependabot.yml | 522 +++++++++++++++++++++++++++++++++++++++++ CHANGELOG.md | 2 + 2 files changed, 524 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 9682461d9e110..07755ef69c6a3 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,869 +4,1391 @@ updates: package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /benchmarks/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /buildSrc/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /buildSrc/reaper/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /buildSrc/src/integTest/resources/org/opensearch/gradle/internal/fake_git/remote/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /buildSrc/src/integTest/resources/org/opensearch/gradle/internal/fake_git/remote/distribution/archives/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /buildSrc/src/integTest/resources/org/opensearch/gradle/internal/fake_git/remote/distribution/archives/darwin-tar/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /buildSrc/src/integTest/resources/org/opensearch/gradle/internal/fake_git/remote/distribution/archives/oss-darwin-tar/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /buildSrc/src/integTest/resources/org/opensearch/gradle/internal/fake_git/remote/distribution/bwc/bugfix/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /buildSrc/src/integTest/resources/org/opensearch/gradle/internal/fake_git/remote/distribution/bwc/minor/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /buildSrc/src/testKit/opensearch-build-resources/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /buildSrc/src/testKit/opensearch.build/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /buildSrc/src/testKit/reaper/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /buildSrc/src/testKit/symbolic-link-preserving-tar/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /buildSrc/src/testKit/testingConventions/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /buildSrc/src/testKit/thirdPartyAudit/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /buildSrc/src/testKit/thirdPartyAudit/sample_jars/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /client/benchmark/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /client/client-benchmark-noop-api-plugin/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /client/rest/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /client/rest-high-level/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /client/sniffer/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /client/test/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /distribution/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /distribution/archives/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /distribution/archives/darwin-tar/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /distribution/archives/integ-test-zip/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /distribution/archives/linux-arm64-tar/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /distribution/archives/linux-tar/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /distribution/archives/no-jdk-darwin-tar/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /distribution/archives/no-jdk-linux-tar/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /distribution/archives/no-jdk-windows-zip/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /distribution/archives/windows-zip/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /distribution/bwc/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /distribution/bwc/bugfix/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /distribution/bwc/maintenance/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /distribution/bwc/minor/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /distribution/bwc/staged/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /distribution/docker/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /distribution/docker/docker-arm64-export/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /distribution/docker/docker-build-context/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /distribution/docker/docker-export/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /distribution/packages/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /distribution/packages/arm64-deb/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /distribution/packages/arm64-rpm/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /distribution/packages/deb/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /distribution/packages/no-jdk-deb/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /distribution/packages/no-jdk-rpm/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /distribution/packages/rpm/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /distribution/tools/java-version-checker/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /distribution/tools/keystore-cli/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /distribution/tools/launchers/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /distribution/tools/plugin-cli/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /distribution/tools/upgrade-cli/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /doc-tools/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /doc-tools/missing-doclet/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /libs/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /libs/cli/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /libs/core/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /libs/dissect/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /libs/geo/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /libs/grok/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /libs/nio/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /libs/plugin-classloader/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /libs/secure-sm/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /libs/ssl-config/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /libs/x-content/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /modules/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /modules/aggs-matrix-stats/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /modules/analysis-common/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /modules/geo/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /modules/ingest-common/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /modules/ingest-geoip/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /modules/ingest-user-agent/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /modules/lang-expression/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /modules/lang-mustache/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /modules/lang-painless/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /modules/lang-painless/spi/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /modules/mapper-extras/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /modules/opensearch-dashboards/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /modules/parent-join/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /modules/percolator/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /modules/rank-eval/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /modules/reindex/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /modules/repository-url/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /modules/systemd/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /modules/transport-netty4/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /plugins/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /plugins/analysis-icu/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /plugins/analysis-kuromoji/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /plugins/analysis-nori/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /plugins/analysis-phonetic/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /plugins/analysis-smartcn/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /plugins/analysis-stempel/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /plugins/analysis-ukrainian/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /plugins/discovery-azure-classic/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /plugins/discovery-ec2/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /plugins/discovery-ec2/qa/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /plugins/discovery-ec2/qa/amazon-ec2/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /plugins/discovery-gce/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /plugins/discovery-gce/qa/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /plugins/discovery-gce/qa/gce/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /plugins/examples/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /plugins/examples/custom-settings/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /plugins/examples/custom-significance-heuristic/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /plugins/examples/custom-suggester/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /plugins/examples/painless-allowlist/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /plugins/examples/rescore/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /plugins/examples/rest-handler/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /plugins/examples/script-expert-scoring/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /plugins/ingest-attachment/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /plugins/mapper-annotated-text/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /plugins/mapper-murmur3/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /plugins/mapper-size/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /plugins/repository-azure/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /plugins/repository-gcs/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /plugins/repository-hdfs/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /plugins/repository-s3/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /plugins/store-smb/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /plugins/transport-nio/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /qa/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /qa/ccs-unavailable-clusters/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /qa/die-with-dignity/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /qa/evil-tests/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /qa/full-cluster-restart/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /qa/logging-config/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /qa/mixed-cluster/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /qa/multi-cluster-search/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /qa/no-bootstrap-tests/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /qa/os/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /qa/os/centos-6/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /qa/os/centos-7/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /qa/os/debian-8/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /qa/os/debian-9/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /qa/os/fedora-28/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /qa/os/fedora-29/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /qa/os/oel-6/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /qa/os/oel-7/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /qa/os/sles-12/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /qa/os/ubuntu-1604/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /qa/os/ubuntu-1804/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /qa/os/windows-2012r2/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /qa/os/windows-2016/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /qa/remote-clusters/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /qa/repository-multi-version/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /qa/rolling-upgrade/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /qa/smoke-test-http/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /qa/smoke-test-ingest-disabled/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /qa/smoke-test-ingest-with-all-dependencies/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /qa/smoke-test-multinode/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /qa/smoke-test-plugins/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /qa/translog-policy/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /qa/unconfigured-node-name/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /qa/verify-version-constants/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /qa/wildfly/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /rest-api-spec/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /sandbox/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /sandbox/libs/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /sandbox/modules/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /sandbox/plugins/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /server/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /test/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /test/external-modules/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /test/external-modules/delayed-aggs/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /test/fixtures/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /test/fixtures/azure-fixture/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /test/fixtures/gcs-fixture/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /test/fixtures/hdfs-fixture/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /test/fixtures/krb5kdc-fixture/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /test/fixtures/minio-fixture/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /test/fixtures/old-elasticsearch/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /test/fixtures/s3-fixture/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /test/framework/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" - directory: /test/logger-usage/ open-pull-requests-limit: 1 package-ecosystem: gradle schedule: interval: weekly + labels: + - "dependabot" + - "dependencies" version: 2 diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b3d54a802e67..1be3d3f53f2d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Added @dreamer-89 as an Opensearch maintainer ([#4342](https://github.com/opensearch-project/OpenSearch/pull/4342)) - Added release notes for 1.3.5 ([#4343](https://github.com/opensearch-project/OpenSearch/pull/4343)) - Added release notes for 2.2.1 ([#4344](https://github.com/opensearch-project/OpenSearch/pull/4344)) +- Label configuration for dependabot PRs ([#4348](https://github.com/opensearch-project/OpenSearch/pull/4348)) ### Changed - Dependency updates (httpcore, mockito, slf4j, httpasyncclient, commons-codec) ([#4308](https://github.com/opensearch-project/OpenSearch/pull/4308)) @@ -32,6 +33,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ## [2.x] ### Added - Github workflow for changelog verification ([#4085](https://github.com/opensearch-project/OpenSearch/pull/4085)) +- Label configuration for dependabot PRs ([#4348](https://github.com/opensearch-project/OpenSearch/pull/4348)) ### Changed From 92337574444e3518e6feff6a2148e8343aa7f2ed Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Wed, 31 Aug 2022 14:28:16 +0530 Subject: [PATCH 065/187] Control peer discovery during decommission --- .../cluster/coordination/Coordinator.java | 28 ++++++++++--------- .../cluster/coordination/JoinHelper.java | 21 +++++++++++++- .../common/settings/ClusterSettings.java | 1 + .../org/opensearch/discovery/PeerFinder.java | 22 ++++++++++++++- .../cluster/coordination/JoinHelperTests.java | 12 ++++++-- 5 files changed, 66 insertions(+), 18 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/coordination/Coordinator.java b/server/src/main/java/org/opensearch/cluster/coordination/Coordinator.java index 1c7e7cd0419e2..9f455142a2aef 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/Coordinator.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/Coordinator.java @@ -208,19 +208,6 @@ public Coordinator( this.onJoinValidators = JoinTaskExecutor.addBuiltInJoinValidators(onJoinValidators); this.singleNodeDiscovery = DiscoveryModule.isSingleNodeDiscovery(settings); this.electionStrategy = electionStrategy; - this.joinHelper = new JoinHelper( - settings, - allocationService, - clusterManagerService, - transportService, - this::getCurrentTerm, - this::getStateForClusterManagerService, - this::handleJoinRequest, - this::joinLeaderInTerm, - this.onJoinValidators, - rerouteService, - nodeHealthService - ); this.persistedStateSupplier = persistedStateSupplier; this.noClusterManagerBlockService = new NoClusterManagerBlockService(settings, clusterSettings); this.lastKnownLeader = Optional.empty(); @@ -244,6 +231,21 @@ public Coordinator( new HandshakingTransportAddressConnector(settings, transportService), configuredHostsResolver ); + this.joinHelper = new JoinHelper( + settings, + allocationService, + clusterManagerService, + transportService, + this::getCurrentTerm, + this::getStateForClusterManagerService, + this::handleJoinRequest, + this::joinLeaderInTerm, + this.onJoinValidators, + rerouteService, + nodeHealthService, + this.peerFinder::onDecommission, + this.peerFinder::onRecommission + ); this.publicationHandler = new PublicationTransportHandler( transportService, namedWriteableRegistry, diff --git a/server/src/main/java/org/opensearch/cluster/coordination/JoinHelper.java b/server/src/main/java/org/opensearch/cluster/coordination/JoinHelper.java index 656e6d220720f..4442ddb3bc3c5 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/JoinHelper.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/JoinHelper.java @@ -42,6 +42,7 @@ import org.opensearch.cluster.ClusterStateTaskListener; import org.opensearch.cluster.NotClusterManagerException; import org.opensearch.cluster.coordination.Coordinator.Mode; +import org.opensearch.cluster.decommission.NodeDecommissionedException; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.routing.RerouteService; @@ -57,6 +58,7 @@ import org.opensearch.monitor.StatusInfo; import org.opensearch.threadpool.ThreadPool; import org.opensearch.threadpool.ThreadPool.Names; +import org.opensearch.transport.RemoteTransportException; import org.opensearch.transport.TransportChannel; import org.opensearch.transport.TransportException; import org.opensearch.transport.TransportRequest; @@ -113,6 +115,10 @@ public class JoinHelper { private final TimeValue joinTimeout; // only used for Zen1 joining private final NodeHealthService nodeHealthService; + public boolean isDecommissioned; + private Runnable onDecommission; + private Runnable onRecommission; + private final Set> pendingOutgoingJoins = Collections.synchronizedSet(new HashSet<>()); private final AtomicReference lastFailedJoinAttempt = new AtomicReference<>(); @@ -130,7 +136,9 @@ public class JoinHelper { Function joinLeaderInTerm, Collection> joinValidators, RerouteService rerouteService, - NodeHealthService nodeHealthService + NodeHealthService nodeHealthService, + Runnable onDecommission, + Runnable onRecommission ) { this.clusterManagerService = clusterManagerService; this.transportService = transportService; @@ -343,11 +351,22 @@ public void handleResponse(Empty response) { logger.debug("successfully joined {} with {}", destination, joinRequest); lastFailedJoinAttempt.set(null); onCompletion.run(); + if (isDecommissioned) { + isDecommissioned = false; + onRecommission.run(); + } } @Override public void handleException(TransportException exp) { pendingOutgoingJoins.remove(dedupKey); + if (exp instanceof RemoteTransportException && (exp.getCause() instanceof NodeDecommissionedException)) { + logger.info("local node is decommissioned. Will not be able to join the cluster"); + if (!isDecommissioned) { + isDecommissioned = true; + onDecommission.run(); + } + } logger.info(() -> new ParameterizedMessage("failed to join {} with {}", destination, joinRequest), exp); FailedJoinAttempt attempt = new FailedJoinAttempt(destination, joinRequest, exp); attempt.logNow(); diff --git a/server/src/main/java/org/opensearch/common/settings/ClusterSettings.java b/server/src/main/java/org/opensearch/common/settings/ClusterSettings.java index 971fb518ff1da..826500ddcf48d 100644 --- a/server/src/main/java/org/opensearch/common/settings/ClusterSettings.java +++ b/server/src/main/java/org/opensearch/common/settings/ClusterSettings.java @@ -533,6 +533,7 @@ public void apply(Settings value, Settings current, Settings previous) { PersistentTasksClusterService.CLUSTER_TASKS_ALLOCATION_RECHECK_INTERVAL_SETTING, EnableAssignmentDecider.CLUSTER_TASKS_ALLOCATION_ENABLE_SETTING, PeerFinder.DISCOVERY_FIND_PEERS_INTERVAL_SETTING, + PeerFinder.DISCOVERY_FIND_PEERS_INTERVAL_DURING_DECOMMISSION_SETTING, PeerFinder.DISCOVERY_REQUEST_PEERS_TIMEOUT_SETTING, ClusterFormationFailureHelper.DISCOVERY_CLUSTER_FORMATION_WARNING_TIMEOUT_SETTING, ElectionSchedulerFactory.ELECTION_INITIAL_TIMEOUT_SETTING, diff --git a/server/src/main/java/org/opensearch/discovery/PeerFinder.java b/server/src/main/java/org/opensearch/discovery/PeerFinder.java index a601a6fbe4d82..67727459fdcde 100644 --- a/server/src/main/java/org/opensearch/discovery/PeerFinder.java +++ b/server/src/main/java/org/opensearch/discovery/PeerFinder.java @@ -84,6 +84,14 @@ public abstract class PeerFinder { Setting.Property.NodeScope ); + // the time between attempts to find all peers when node is in decommissioned state, default set to 2 minutes + public static final Setting DISCOVERY_FIND_PEERS_INTERVAL_DURING_DECOMMISSION_SETTING = Setting.timeSetting( + "discovery.find_peers_interval_during_decommission", + TimeValue.timeValueMinutes(2L), + TimeValue.timeValueMillis(1000), + Setting.Property.NodeScope + ); + public static final Setting DISCOVERY_REQUEST_PEERS_TIMEOUT_SETTING = Setting.timeSetting( "discovery.request_peers_timeout", TimeValue.timeValueMillis(3000), @@ -91,7 +99,8 @@ public abstract class PeerFinder { Setting.Property.NodeScope ); - private final TimeValue findPeersInterval; + private final Settings settings; + private TimeValue findPeersInterval; private final TimeValue requestPeersTimeout; private final Object mutex = new Object(); @@ -112,6 +121,7 @@ public PeerFinder( TransportAddressConnector transportAddressConnector, ConfiguredHostsResolver configuredHostsResolver ) { + this.settings = settings; findPeersInterval = DISCOVERY_FIND_PEERS_INTERVAL_SETTING.get(settings); requestPeersTimeout = DISCOVERY_REQUEST_PEERS_TIMEOUT_SETTING.get(settings); this.transportService = transportService; @@ -156,6 +166,16 @@ public void deactivate(DiscoveryNode leader) { } } + public void onDecommission() { + findPeersInterval = DISCOVERY_FIND_PEERS_INTERVAL_DURING_DECOMMISSION_SETTING.get(settings); + logger.info("setting findPeersInterval to [{}], due to decommissioning", findPeersInterval); + } + + public void onRecommission() { + findPeersInterval = DISCOVERY_FIND_PEERS_INTERVAL_SETTING.get(settings); + logger.info("setting findPeersInterval to [{}], due to recommissioning", findPeersInterval); + } + // exposed to subclasses for testing protected final boolean holdsLock() { return Thread.holdsLock(mutex); diff --git a/server/src/test/java/org/opensearch/cluster/coordination/JoinHelperTests.java b/server/src/test/java/org/opensearch/cluster/coordination/JoinHelperTests.java index a3c945cdbac3a..a43af882975b8 100644 --- a/server/src/test/java/org/opensearch/cluster/coordination/JoinHelperTests.java +++ b/server/src/test/java/org/opensearch/cluster/coordination/JoinHelperTests.java @@ -90,7 +90,9 @@ public void testJoinDeduplication() { startJoinRequest -> { throw new AssertionError(); }, Collections.emptyList(), (s, p, r) -> {}, - () -> new StatusInfo(HEALTHY, "info") + () -> new StatusInfo(HEALTHY, "info"), + ()-> {}, + () -> {} ); transportService.start(); @@ -230,7 +232,9 @@ private void assertJoinValidationRejectsMismatchedClusterUUID(String actionName, startJoinRequest -> { throw new AssertionError(); }, Collections.emptyList(), (s, p, r) -> {}, - null + null, + () -> {}, + () -> {} ); // registers request handler transportService.start(); transportService.acceptIncomingRequests(); @@ -284,7 +288,9 @@ public void testJoinFailureOnUnhealthyNodes() { startJoinRequest -> { throw new AssertionError(); }, Collections.emptyList(), (s, p, r) -> {}, - () -> nodeHealthServiceStatus.get() + () -> nodeHealthServiceStatus.get(), + () -> {}, + () -> {} ); transportService.start(); From 8eb27f0d92e2e45771beb068d8cfedf33629e563 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Wed, 31 Aug 2022 17:32:09 +0530 Subject: [PATCH 066/187] Change runnable to action listener --- .../cluster/coordination/Coordinator.java | 3 +-- .../cluster/coordination/JoinHelper.java | 11 ++++---- .../org/opensearch/discovery/PeerFinder.java | 26 ++++++++++++------- .../cluster/coordination/JoinHelperTests.java | 11 ++++---- 4 files changed, 27 insertions(+), 24 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/coordination/Coordinator.java b/server/src/main/java/org/opensearch/cluster/coordination/Coordinator.java index 9f455142a2aef..cb99f02bbf03f 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/Coordinator.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/Coordinator.java @@ -243,8 +243,7 @@ public Coordinator( this.onJoinValidators, rerouteService, nodeHealthService, - this.peerFinder::onDecommission, - this.peerFinder::onRecommission + peerFinder.nodeCommissionedListener() ); this.publicationHandler = new PublicationTransportHandler( transportService, diff --git a/server/src/main/java/org/opensearch/cluster/coordination/JoinHelper.java b/server/src/main/java/org/opensearch/cluster/coordination/JoinHelper.java index 4442ddb3bc3c5..9e30b9a3f7ee3 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/JoinHelper.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/JoinHelper.java @@ -116,8 +116,7 @@ public class JoinHelper { private final NodeHealthService nodeHealthService; public boolean isDecommissioned; - private Runnable onDecommission; - private Runnable onRecommission; + private final ActionListener nodeCommissionedListener; private final Set> pendingOutgoingJoins = Collections.synchronizedSet(new HashSet<>()); @@ -137,13 +136,13 @@ public class JoinHelper { Collection> joinValidators, RerouteService rerouteService, NodeHealthService nodeHealthService, - Runnable onDecommission, - Runnable onRecommission + ActionListener nodeCommissionedListener ) { this.clusterManagerService = clusterManagerService; this.transportService = transportService; this.nodeHealthService = nodeHealthService; this.joinTimeout = JOIN_TIMEOUT_SETTING.get(settings); + this.nodeCommissionedListener = nodeCommissionedListener; this.joinTaskExecutorGenerator = () -> new JoinTaskExecutor(settings, allocationService, logger, rerouteService, transportService) { private final long term = currentTermSupplier.getAsLong(); @@ -353,7 +352,7 @@ public void handleResponse(Empty response) { onCompletion.run(); if (isDecommissioned) { isDecommissioned = false; - onRecommission.run(); + nodeCommissionedListener.onResponse(null); } } @@ -364,7 +363,7 @@ public void handleException(TransportException exp) { logger.info("local node is decommissioned. Will not be able to join the cluster"); if (!isDecommissioned) { isDecommissioned = true; - onDecommission.run(); + nodeCommissionedListener.onFailure(exp); } } logger.info(() -> new ParameterizedMessage("failed to join {} with {}", destination, joinRequest), exp); diff --git a/server/src/main/java/org/opensearch/discovery/PeerFinder.java b/server/src/main/java/org/opensearch/discovery/PeerFinder.java index 67727459fdcde..5f89e681f5526 100644 --- a/server/src/main/java/org/opensearch/discovery/PeerFinder.java +++ b/server/src/main/java/org/opensearch/discovery/PeerFinder.java @@ -138,6 +138,22 @@ public PeerFinder( ); } + public ActionListener nodeCommissionedListener() { + return new ActionListener() { + @Override + public void onResponse(Void unused) { + logger.info("setting findPeersInterval to [{}], due to recommissioning", findPeersInterval); + findPeersInterval = DISCOVERY_FIND_PEERS_INTERVAL_SETTING.get(settings); + } + + @Override + public void onFailure(Exception e) { + logger.info("setting findPeersInterval to [{}], due to decommissioning", findPeersInterval); + findPeersInterval = DISCOVERY_FIND_PEERS_INTERVAL_DURING_DECOMMISSION_SETTING.get(settings); + } + }; + } + public void activate(final DiscoveryNodes lastAcceptedNodes) { logger.trace("activating with {}", lastAcceptedNodes); @@ -166,16 +182,6 @@ public void deactivate(DiscoveryNode leader) { } } - public void onDecommission() { - findPeersInterval = DISCOVERY_FIND_PEERS_INTERVAL_DURING_DECOMMISSION_SETTING.get(settings); - logger.info("setting findPeersInterval to [{}], due to decommissioning", findPeersInterval); - } - - public void onRecommission() { - findPeersInterval = DISCOVERY_FIND_PEERS_INTERVAL_SETTING.get(settings); - logger.info("setting findPeersInterval to [{}], due to recommissioning", findPeersInterval); - } - // exposed to subclasses for testing protected final boolean holdsLock() { return Thread.holdsLock(mutex); diff --git a/server/src/test/java/org/opensearch/cluster/coordination/JoinHelperTests.java b/server/src/test/java/org/opensearch/cluster/coordination/JoinHelperTests.java index a43af882975b8..50e18f25aad5b 100644 --- a/server/src/test/java/org/opensearch/cluster/coordination/JoinHelperTests.java +++ b/server/src/test/java/org/opensearch/cluster/coordination/JoinHelperTests.java @@ -33,6 +33,7 @@ import org.apache.logging.log4j.Level; import org.opensearch.Version; +import org.opensearch.action.ActionListener; import org.opensearch.action.ActionListenerResponseHandler; import org.opensearch.action.support.PlainActionFuture; import org.opensearch.cluster.ClusterName; @@ -55,6 +56,7 @@ import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; +import static org.mockito.Mockito.mock; import static org.opensearch.monitor.StatusInfo.Status.HEALTHY; import static org.opensearch.monitor.StatusInfo.Status.UNHEALTHY; import static org.opensearch.node.Node.NODE_NAME_SETTING; @@ -91,8 +93,7 @@ public void testJoinDeduplication() { Collections.emptyList(), (s, p, r) -> {}, () -> new StatusInfo(HEALTHY, "info"), - ()-> {}, - () -> {} + mock(ActionListener.class) ); transportService.start(); @@ -233,8 +234,7 @@ private void assertJoinValidationRejectsMismatchedClusterUUID(String actionName, Collections.emptyList(), (s, p, r) -> {}, null, - () -> {}, - () -> {} + mock(ActionListener.class) ); // registers request handler transportService.start(); transportService.acceptIncomingRequests(); @@ -289,8 +289,7 @@ public void testJoinFailureOnUnhealthyNodes() { Collections.emptyList(), (s, p, r) -> {}, () -> nodeHealthServiceStatus.get(), - () -> {}, - () -> {} + mock(ActionListener.class) ); transportService.start(); From d72861f9de379d2a263948232947e9b95aefa962 Mon Sep 17 00:00:00 2001 From: Andriy Redko Date: Wed, 31 Aug 2022 09:07:44 -0400 Subject: [PATCH 067/187] Support for HTTP/2 (server-side) (#3847) * Support for HTTP/2 (server-side) Signed-off-by: Andriy Redko * Addressing code review comments Signed-off-by: Andriy Redko * Added HTTP/1.1 channel configuration Signed-off-by: Andriy Redko * Addressing code review comments Signed-off-by: Andriy Redko * Update pul request URL in CHANGELOG.md Signed-off-by: Andriy Redko Signed-off-by: Andriy Redko --- CHANGELOG.md | 1 + modules/transport-netty4/build.gradle | 1 + .../netty-codec-http2-4.1.79.Final.jar.sha1 | 1 + .../opensearch/http/netty4/Netty4Http2IT.java | 62 ++++++ .../netty4/Netty4HttpRequestSizeLimitIT.java | 4 +- .../http/netty4/Netty4PipeliningIT.java | 2 +- .../http/netty4/Netty4HttpChannel.java | 13 ++ .../netty4/Netty4HttpServerTransport.java | 160 ++++++++++++-- .../http/netty4/Netty4BadRequestTests.java | 2 +- .../http/netty4/Netty4HttpClient.java | 197 +++++++++++++++++- .../Netty4HttpServerPipeliningTests.java | 7 +- .../Netty4HttpServerTransportTests.java | 10 +- 12 files changed, 428 insertions(+), 32 deletions(-) create mode 100644 modules/transport-netty4/licenses/netty-codec-http2-4.1.79.Final.jar.sha1 create mode 100644 modules/transport-netty4/src/internalClusterTest/java/org/opensearch/http/netty4/Netty4Http2IT.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 1be3d3f53f2d6..8e7fa8b5547f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Added release notes for 1.3.5 ([#4343](https://github.com/opensearch-project/OpenSearch/pull/4343)) - Added release notes for 2.2.1 ([#4344](https://github.com/opensearch-project/OpenSearch/pull/4344)) - Label configuration for dependabot PRs ([#4348](https://github.com/opensearch-project/OpenSearch/pull/4348)) +- Support for HTTP/2 (server-side) ([#3847](https://github.com/opensearch-project/OpenSearch/pull/3847)) ### Changed - Dependency updates (httpcore, mockito, slf4j, httpasyncclient, commons-codec) ([#4308](https://github.com/opensearch-project/OpenSearch/pull/4308)) diff --git a/modules/transport-netty4/build.gradle b/modules/transport-netty4/build.gradle index b72cb6d868d79..5d2047d7f18a2 100644 --- a/modules/transport-netty4/build.gradle +++ b/modules/transport-netty4/build.gradle @@ -58,6 +58,7 @@ dependencies { api "io.netty:netty-buffer:${versions.netty}" api "io.netty:netty-codec:${versions.netty}" api "io.netty:netty-codec-http:${versions.netty}" + api "io.netty:netty-codec-http2:${versions.netty}" api "io.netty:netty-common:${versions.netty}" api "io.netty:netty-handler:${versions.netty}" api "io.netty:netty-resolver:${versions.netty}" diff --git a/modules/transport-netty4/licenses/netty-codec-http2-4.1.79.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-codec-http2-4.1.79.Final.jar.sha1 new file mode 100644 index 0000000000000..f2989024cfce1 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-codec-http2-4.1.79.Final.jar.sha1 @@ -0,0 +1 @@ +0eeffab0cd5efb699d5e4ab9b694d32fef6694b3 \ No newline at end of file diff --git a/modules/transport-netty4/src/internalClusterTest/java/org/opensearch/http/netty4/Netty4Http2IT.java b/modules/transport-netty4/src/internalClusterTest/java/org/opensearch/http/netty4/Netty4Http2IT.java new file mode 100644 index 0000000000000..1424b392af8e7 --- /dev/null +++ b/modules/transport-netty4/src/internalClusterTest/java/org/opensearch/http/netty4/Netty4Http2IT.java @@ -0,0 +1,62 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.http.netty4; + +import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.util.ReferenceCounted; +import org.opensearch.OpenSearchNetty4IntegTestCase; +import org.opensearch.common.transport.TransportAddress; +import org.opensearch.http.HttpServerTransport; +import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; +import org.opensearch.test.OpenSearchIntegTestCase.Scope; + +import java.util.Collection; +import java.util.Locale; +import java.util.stream.IntStream; + +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.hasSize; + +@ClusterScope(scope = Scope.TEST, supportsDedicatedMasters = false, numDataNodes = 1) +public class Netty4Http2IT extends OpenSearchNetty4IntegTestCase { + + @Override + protected boolean addMockHttpTransport() { + return false; // enable http + } + + public void testThatNettyHttpServerSupportsHttp2() throws Exception { + String[] requests = new String[] { "/", "/_nodes/stats", "/", "/_cluster/state", "/" }; + + HttpServerTransport httpServerTransport = internalCluster().getInstance(HttpServerTransport.class); + TransportAddress[] boundAddresses = httpServerTransport.boundAddress().boundAddresses(); + TransportAddress transportAddress = randomFrom(boundAddresses); + + try (Netty4HttpClient nettyHttpClient = Netty4HttpClient.http2()) { + Collection responses = nettyHttpClient.get(transportAddress.address(), requests); + try { + assertThat(responses, hasSize(5)); + + Collection opaqueIds = Netty4HttpClient.returnOpaqueIds(responses); + assertOpaqueIdsInAnyOrder(opaqueIds); + } finally { + responses.forEach(ReferenceCounted::release); + } + } + } + + private void assertOpaqueIdsInAnyOrder(Collection opaqueIds) { + // check if opaque ids are present in any order, since for HTTP/2 we use streaming (no head of line blocking) + // and responses may come back at any order + int i = 0; + String msg = String.format(Locale.ROOT, "Expected list of opaque ids to be in any order, got [%s]", opaqueIds); + assertThat(msg, opaqueIds, containsInAnyOrder(IntStream.range(0, 5).mapToObj(Integer::toString).toArray())); + } + +} diff --git a/modules/transport-netty4/src/internalClusterTest/java/org/opensearch/http/netty4/Netty4HttpRequestSizeLimitIT.java b/modules/transport-netty4/src/internalClusterTest/java/org/opensearch/http/netty4/Netty4HttpRequestSizeLimitIT.java index 08df9259d475f..db76c0b145840 100644 --- a/modules/transport-netty4/src/internalClusterTest/java/org/opensearch/http/netty4/Netty4HttpRequestSizeLimitIT.java +++ b/modules/transport-netty4/src/internalClusterTest/java/org/opensearch/http/netty4/Netty4HttpRequestSizeLimitIT.java @@ -100,7 +100,7 @@ public void testLimitsInFlightRequests() throws Exception { HttpServerTransport httpServerTransport = internalCluster().getInstance(HttpServerTransport.class); TransportAddress transportAddress = randomFrom(httpServerTransport.boundAddress().boundAddresses()); - try (Netty4HttpClient nettyHttpClient = new Netty4HttpClient()) { + try (Netty4HttpClient nettyHttpClient = Netty4HttpClient.http()) { Collection singleResponse = nettyHttpClient.post(transportAddress.address(), requests.subList(0, 1)); try { assertThat(singleResponse, hasSize(1)); @@ -130,7 +130,7 @@ public void testDoesNotLimitExcludedRequests() throws Exception { HttpServerTransport httpServerTransport = internalCluster().getInstance(HttpServerTransport.class); TransportAddress transportAddress = randomFrom(httpServerTransport.boundAddress().boundAddresses()); - try (Netty4HttpClient nettyHttpClient = new Netty4HttpClient()) { + try (Netty4HttpClient nettyHttpClient = Netty4HttpClient.http()) { Collection responses = nettyHttpClient.put(transportAddress.address(), requestUris); try { assertThat(responses, hasSize(requestUris.size())); diff --git a/modules/transport-netty4/src/internalClusterTest/java/org/opensearch/http/netty4/Netty4PipeliningIT.java b/modules/transport-netty4/src/internalClusterTest/java/org/opensearch/http/netty4/Netty4PipeliningIT.java index 2bd1fa07f8afc..96193b0ecb954 100644 --- a/modules/transport-netty4/src/internalClusterTest/java/org/opensearch/http/netty4/Netty4PipeliningIT.java +++ b/modules/transport-netty4/src/internalClusterTest/java/org/opensearch/http/netty4/Netty4PipeliningIT.java @@ -61,7 +61,7 @@ public void testThatNettyHttpServerSupportsPipelining() throws Exception { TransportAddress[] boundAddresses = httpServerTransport.boundAddress().boundAddresses(); TransportAddress transportAddress = randomFrom(boundAddresses); - try (Netty4HttpClient nettyHttpClient = new Netty4HttpClient()) { + try (Netty4HttpClient nettyHttpClient = Netty4HttpClient.http()) { Collection responses = nettyHttpClient.get(transportAddress.address(), requests); try { assertThat(responses, hasSize(5)); diff --git a/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpChannel.java b/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpChannel.java index 66d60032d11a8..2dd7aaf41986f 100644 --- a/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpChannel.java +++ b/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpChannel.java @@ -33,7 +33,10 @@ package org.opensearch.http.netty4; import io.netty.channel.Channel; +import io.netty.channel.ChannelPipeline; + import org.opensearch.action.ActionListener; +import org.opensearch.common.Nullable; import org.opensearch.common.concurrent.CompletableContext; import org.opensearch.http.HttpChannel; import org.opensearch.http.HttpResponse; @@ -45,9 +48,15 @@ public class Netty4HttpChannel implements HttpChannel { private final Channel channel; private final CompletableContext closeContext = new CompletableContext<>(); + private final ChannelPipeline inboundPipeline; Netty4HttpChannel(Channel channel) { + this(channel, null); + } + + Netty4HttpChannel(Channel channel, ChannelPipeline inboundPipeline) { this.channel = channel; + this.inboundPipeline = inboundPipeline; Netty4TcpChannel.addListener(this.channel.closeFuture(), closeContext); } @@ -81,6 +90,10 @@ public void close() { channel.close(); } + public @Nullable ChannelPipeline inboundPipeline() { + return inboundPipeline; + } + public Channel getNettyChannel() { return channel; } diff --git a/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpServerTransport.java b/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpServerTransport.java index decab45ffca38..1e0a4d89f2fd5 100644 --- a/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpServerTransport.java +++ b/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpServerTransport.java @@ -40,18 +40,36 @@ import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; +import io.netty.channel.ChannelPipeline; import io.netty.channel.FixedRecvByteBufAllocator; import io.netty.channel.RecvByteBufAllocator; +import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.socket.nio.NioChannelOption; import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.http.HttpContentCompressor; import io.netty.handler.codec.http.HttpContentDecompressor; +import io.netty.handler.codec.http.HttpMessage; import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpRequestDecoder; import io.netty.handler.codec.http.HttpResponseEncoder; +import io.netty.handler.codec.http.HttpServerCodec; +import io.netty.handler.codec.http.HttpServerUpgradeHandler; +import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeCodec; +import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeCodecFactory; +import io.netty.handler.codec.http2.CleartextHttp2ServerUpgradeHandler; +import io.netty.handler.codec.http2.Http2CodecUtil; +import io.netty.handler.codec.http2.Http2FrameCodecBuilder; +import io.netty.handler.codec.http2.Http2MultiplexHandler; +import io.netty.handler.codec.http2.Http2ServerUpgradeCodec; +import io.netty.handler.codec.http2.Http2StreamFrameToHttpObjectCodec; +import io.netty.handler.logging.LogLevel; +import io.netty.handler.logging.LoggingHandler; import io.netty.handler.timeout.ReadTimeoutException; import io.netty.handler.timeout.ReadTimeoutHandler; +import io.netty.util.AsciiString; import io.netty.util.AttributeKey; +import io.netty.util.ReferenceCountUtil; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.opensearch.ExceptionsHelper; @@ -335,38 +353,152 @@ protected HttpChannelHandler(final Netty4HttpServerTransport transport, final Ht this.responseCreator = new Netty4HttpResponseCreator(); } + public ChannelHandler getRequestHandler() { + return requestHandler; + } + @Override protected void initChannel(Channel ch) throws Exception { Netty4HttpChannel nettyHttpChannel = new Netty4HttpChannel(ch); ch.attr(HTTP_CHANNEL_KEY).set(nettyHttpChannel); ch.pipeline().addLast("byte_buf_sizer", byteBufSizer); ch.pipeline().addLast("read_timeout", new ReadTimeoutHandler(transport.readTimeoutMillis, TimeUnit.MILLISECONDS)); + + configurePipeline(ch); + transport.serverAcceptedChannel(nettyHttpChannel); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + ExceptionsHelper.maybeDieOnAnotherThread(cause); + super.exceptionCaught(ctx, cause); + } + + protected void configurePipeline(Channel ch) { + final UpgradeCodecFactory upgradeCodecFactory = new UpgradeCodecFactory() { + @Override + public UpgradeCodec newUpgradeCodec(CharSequence protocol) { + if (AsciiString.contentEquals(Http2CodecUtil.HTTP_UPGRADE_PROTOCOL_NAME, protocol)) { + return new Http2ServerUpgradeCodec( + Http2FrameCodecBuilder.forServer().build(), + new Http2MultiplexHandler(createHttp2ChannelInitializer(ch.pipeline())) + ); + } else { + return null; + } + } + }; + + final HttpServerCodec sourceCodec = new HttpServerCodec( + handlingSettings.getMaxInitialLineLength(), + handlingSettings.getMaxHeaderSize(), + handlingSettings.getMaxChunkSize() + ); + + final HttpServerUpgradeHandler upgradeHandler = new HttpServerUpgradeHandler(sourceCodec, upgradeCodecFactory); + final CleartextHttp2ServerUpgradeHandler cleartextUpgradeHandler = new CleartextHttp2ServerUpgradeHandler( + sourceCodec, + upgradeHandler, + createHttp2ChannelInitializerPriorKnowledge() + ); + + ch.pipeline().addLast(cleartextUpgradeHandler).addLast(new SimpleChannelInboundHandler() { + @Override + protected void channelRead0(ChannelHandlerContext ctx, HttpMessage msg) throws Exception { + final HttpObjectAggregator aggregator = new HttpObjectAggregator(handlingSettings.getMaxContentLength()); + aggregator.setMaxCumulationBufferComponents(transport.maxCompositeBufferComponents); + + // If this handler is hit then no upgrade has been attempted and the client is just talking HTTP + final ChannelPipeline pipeline = ctx.pipeline(); + pipeline.addAfter(ctx.name(), "handler", getRequestHandler()); + pipeline.replace(this, "aggregator", aggregator); + + ch.pipeline().addLast("decoder_compress", new HttpContentDecompressor()); + ch.pipeline().addLast("encoder", new HttpResponseEncoder()); + if (handlingSettings.isCompression()) { + ch.pipeline() + .addAfter("aggregator", "encoder_compress", new HttpContentCompressor(handlingSettings.getCompressionLevel())); + } + ch.pipeline().addBefore("handler", "request_creator", requestCreator); + ch.pipeline().addBefore("handler", "response_creator", responseCreator); + ch.pipeline() + .addBefore("handler", "pipelining", new Netty4HttpPipeliningHandler(logger, transport.pipeliningMaxEvents)); + + ctx.fireChannelRead(ReferenceCountUtil.retain(msg)); + } + }); + } + + protected void configureDefaultHttpPipeline(ChannelPipeline pipeline) { final HttpRequestDecoder decoder = new HttpRequestDecoder( handlingSettings.getMaxInitialLineLength(), handlingSettings.getMaxHeaderSize(), handlingSettings.getMaxChunkSize() ); decoder.setCumulator(ByteToMessageDecoder.COMPOSITE_CUMULATOR); - ch.pipeline().addLast("decoder", decoder); - ch.pipeline().addLast("decoder_compress", new HttpContentDecompressor()); - ch.pipeline().addLast("encoder", new HttpResponseEncoder()); + pipeline.addLast("decoder", decoder); + pipeline.addLast("decoder_compress", new HttpContentDecompressor()); + pipeline.addLast("encoder", new HttpResponseEncoder()); final HttpObjectAggregator aggregator = new HttpObjectAggregator(handlingSettings.getMaxContentLength()); aggregator.setMaxCumulationBufferComponents(transport.maxCompositeBufferComponents); - ch.pipeline().addLast("aggregator", aggregator); + pipeline.addLast("aggregator", aggregator); if (handlingSettings.isCompression()) { - ch.pipeline().addLast("encoder_compress", new HttpContentCompressor(handlingSettings.getCompressionLevel())); + pipeline.addLast("encoder_compress", new HttpContentCompressor(handlingSettings.getCompressionLevel())); } - ch.pipeline().addLast("request_creator", requestCreator); - ch.pipeline().addLast("response_creator", responseCreator); - ch.pipeline().addLast("pipelining", new Netty4HttpPipeliningHandler(logger, transport.pipeliningMaxEvents)); - ch.pipeline().addLast("handler", requestHandler); - transport.serverAcceptedChannel(nettyHttpChannel); + pipeline.addLast("request_creator", requestCreator); + pipeline.addLast("response_creator", responseCreator); + pipeline.addLast("pipelining", new Netty4HttpPipeliningHandler(logger, transport.pipeliningMaxEvents)); + pipeline.addLast("handler", requestHandler); } - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - ExceptionsHelper.maybeDieOnAnotherThread(cause); - super.exceptionCaught(ctx, cause); + protected void configureDefaultHttp2Pipeline(ChannelPipeline pipeline) { + pipeline.addLast(Http2FrameCodecBuilder.forServer().build()) + .addLast(new Http2MultiplexHandler(createHttp2ChannelInitializer(pipeline))); + } + + private ChannelInitializer createHttp2ChannelInitializerPriorKnowledge() { + return new ChannelInitializer() { + @Override + protected void initChannel(Channel childChannel) throws Exception { + configureDefaultHttp2Pipeline(childChannel.pipeline()); + } + }; + } + + /** + * Http2MultiplexHandler creates new pipeline, we are preserving the old one in case some handlers need to be + * access (like for example opensearch-security plugin which accesses SSL handlers). + */ + private ChannelInitializer createHttp2ChannelInitializer(ChannelPipeline inboundPipeline) { + return new ChannelInitializer() { + @Override + protected void initChannel(Channel childChannel) throws Exception { + final Netty4HttpChannel nettyHttpChannel = new Netty4HttpChannel(childChannel, inboundPipeline); + childChannel.attr(HTTP_CHANNEL_KEY).set(nettyHttpChannel); + + final HttpObjectAggregator aggregator = new HttpObjectAggregator(handlingSettings.getMaxContentLength()); + aggregator.setMaxCumulationBufferComponents(transport.maxCompositeBufferComponents); + + childChannel.pipeline() + .addLast(new LoggingHandler(LogLevel.DEBUG)) + .addLast(new Http2StreamFrameToHttpObjectCodec(true)) + .addLast("byte_buf_sizer", byteBufSizer) + .addLast("read_timeout", new ReadTimeoutHandler(transport.readTimeoutMillis, TimeUnit.MILLISECONDS)) + .addLast("decoder_decompress", new HttpContentDecompressor()); + + if (handlingSettings.isCompression()) { + childChannel.pipeline() + .addLast("encoder_compress", new HttpContentCompressor(handlingSettings.getCompressionLevel())); + } + + childChannel.pipeline() + .addLast("aggregator", aggregator) + .addLast("request_creator", requestCreator) + .addLast("response_creator", responseCreator) + .addLast("pipelining", new Netty4HttpPipeliningHandler(logger, transport.pipeliningMaxEvents)) + .addLast("handler", getRequestHandler()); + } + }; } } diff --git a/modules/transport-netty4/src/test/java/org/opensearch/http/netty4/Netty4BadRequestTests.java b/modules/transport-netty4/src/test/java/org/opensearch/http/netty4/Netty4BadRequestTests.java index a0100930c7dcb..c18fe6efc4736 100644 --- a/modules/transport-netty4/src/test/java/org/opensearch/http/netty4/Netty4BadRequestTests.java +++ b/modules/transport-netty4/src/test/java/org/opensearch/http/netty4/Netty4BadRequestTests.java @@ -117,7 +117,7 @@ public void dispatchBadRequest(RestChannel channel, ThreadContext threadContext, httpServerTransport.start(); final TransportAddress transportAddress = randomFrom(httpServerTransport.boundAddress().boundAddresses()); - try (Netty4HttpClient nettyHttpClient = new Netty4HttpClient()) { + try (Netty4HttpClient nettyHttpClient = Netty4HttpClient.http()) { final Collection responses = nettyHttpClient.get( transportAddress.address(), "/_cluster/settings?pretty=%" diff --git a/modules/transport-netty4/src/test/java/org/opensearch/http/netty4/Netty4HttpClient.java b/modules/transport-netty4/src/test/java/org/opensearch/http/netty4/Netty4HttpClient.java index 57f95a022a33f..6fdd698c117f2 100644 --- a/modules/transport-netty4/src/test/java/org/opensearch/http/netty4/Netty4HttpClient.java +++ b/modules/transport-netty4/src/test/java/org/opensearch/http/netty4/Netty4HttpClient.java @@ -37,14 +37,19 @@ import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandler; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; +import io.netty.channel.ChannelPromise; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.handler.codec.http.HttpClientCodec; +import io.netty.handler.codec.http.HttpClientUpgradeHandler; import io.netty.handler.codec.http.HttpContentDecompressor; import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpMethod; @@ -55,6 +60,17 @@ import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.HttpResponseDecoder; import io.netty.handler.codec.http.HttpVersion; +import io.netty.handler.codec.http2.DefaultHttp2Connection; +import io.netty.handler.codec.http2.DelegatingDecompressorFrameListener; +import io.netty.handler.codec.http2.Http2ClientUpgradeCodec; +import io.netty.handler.codec.http2.Http2Connection; +import io.netty.handler.codec.http2.Http2Settings; +import io.netty.handler.codec.http2.HttpConversionUtil; +import io.netty.handler.codec.http2.HttpToHttp2ConnectionHandler; +import io.netty.handler.codec.http2.HttpToHttp2ConnectionHandlerBuilder; +import io.netty.handler.codec.http2.InboundHttp2ToHttpAdapterBuilder; +import io.netty.util.AttributeKey; + import org.opensearch.common.collect.Tuple; import org.opensearch.common.unit.ByteSizeUnit; import org.opensearch.common.unit.ByteSizeValue; @@ -70,6 +86,7 @@ import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.function.BiFunction; import static io.netty.handler.codec.http.HttpHeaderNames.HOST; import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; @@ -97,11 +114,32 @@ static Collection returnOpaqueIds(Collection responses } private final Bootstrap clientBootstrap; + private final BiFunction, AwaitableChannelInitializer> handlerFactory; + + Netty4HttpClient( + Bootstrap clientBootstrap, + BiFunction, AwaitableChannelInitializer> handlerFactory + ) { + this.clientBootstrap = clientBootstrap; + this.handlerFactory = handlerFactory; + } + + static Netty4HttpClient http() { + return new Netty4HttpClient( + new Bootstrap().channel(NettyAllocator.getChannelType()) + .option(ChannelOption.ALLOCATOR, NettyAllocator.getAllocator()) + .group(new NioEventLoopGroup(1)), + CountDownLatchHandlerHttp::new + ); + } - Netty4HttpClient() { - clientBootstrap = new Bootstrap().channel(NettyAllocator.getChannelType()) - .option(ChannelOption.ALLOCATOR, NettyAllocator.getAllocator()) - .group(new NioEventLoopGroup(1)); + static Netty4HttpClient http2() { + return new Netty4HttpClient( + new Bootstrap().channel(NettyAllocator.getChannelType()) + .option(ChannelOption.ALLOCATOR, NettyAllocator.getAllocator()) + .group(new NioEventLoopGroup(1)), + CountDownLatchHandlerHttp2::new + ); } public List get(SocketAddress remoteAddress, String... uris) throws InterruptedException { @@ -110,6 +148,7 @@ public List get(SocketAddress remoteAddress, String... uris) t final HttpRequest httpRequest = new DefaultFullHttpRequest(HTTP_1_1, HttpMethod.GET, uris[i]); httpRequest.headers().add(HOST, "localhost"); httpRequest.headers().add("X-Opaque-ID", String.valueOf(i)); + httpRequest.headers().add(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), "http"); requests.add(httpRequest); } return sendRequests(remoteAddress, requests); @@ -143,6 +182,7 @@ private List processRequestsWithBody( request.headers().add(HttpHeaderNames.HOST, "localhost"); request.headers().add(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes()); request.headers().add(HttpHeaderNames.CONTENT_TYPE, "application/json"); + request.headers().add(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), "http"); requests.add(request); } return sendRequests(remoteAddress, requests); @@ -153,12 +193,14 @@ private synchronized List sendRequests(final SocketAddress rem final CountDownLatch latch = new CountDownLatch(requests.size()); final List content = Collections.synchronizedList(new ArrayList<>(requests.size())); - clientBootstrap.handler(new CountDownLatchHandler(latch, content)); + final AwaitableChannelInitializer handler = handlerFactory.apply(latch, content); + clientBootstrap.handler(handler); ChannelFuture channelFuture = null; try { channelFuture = clientBootstrap.connect(remoteAddress); channelFuture.sync(); + handler.await(); for (HttpRequest request : requests) { channelFuture.channel().writeAndFlush(request); @@ -184,12 +226,12 @@ public void close() { /** * helper factory which adds returned data to a list and uses a count down latch to decide when done */ - private static class CountDownLatchHandler extends ChannelInitializer { + private static class CountDownLatchHandlerHttp extends AwaitableChannelInitializer { private final CountDownLatch latch; private final Collection content; - CountDownLatchHandler(final CountDownLatch latch, final Collection content) { + CountDownLatchHandlerHttp(final CountDownLatch latch, final Collection content) { this.latch = latch; this.content = content; } @@ -222,4 +264,145 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws E } + /** + * The channel initializer with the ability to await for initialization to be completed + * + */ + private static abstract class AwaitableChannelInitializer extends ChannelInitializer { + void await() { + // do nothing + } + } + + /** + * helper factory which adds returned data to a list and uses a count down latch to decide when done + */ + private static class CountDownLatchHandlerHttp2 extends AwaitableChannelInitializer { + + private final CountDownLatch latch; + private final Collection content; + private Http2SettingsHandler settingsHandler; + + CountDownLatchHandlerHttp2(final CountDownLatch latch, final Collection content) { + this.latch = latch; + this.content = content; + } + + @Override + protected void initChannel(SocketChannel ch) { + final int maxContentLength = new ByteSizeValue(100, ByteSizeUnit.MB).bytesAsInt(); + final Http2Connection connection = new DefaultHttp2Connection(false); + settingsHandler = new Http2SettingsHandler(ch.newPromise()); + + final ChannelInboundHandler responseHandler = new SimpleChannelInboundHandler() { + @Override + protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) { + final FullHttpResponse response = (FullHttpResponse) msg; + + // this is upgrade request, skipping it over + if (Boolean.TRUE.equals(ctx.channel().attr(AttributeKey.valueOf("upgrade")).getAndRemove())) { + return; + } + + // We copy the buffer manually to avoid a huge allocation on a pooled allocator. We have + // a test that tracks huge allocations, so we want to avoid them in this test code. + ByteBuf newContent = Unpooled.copiedBuffer(((FullHttpResponse) msg).content()); + content.add(response.replace(newContent)); + latch.countDown(); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + super.exceptionCaught(ctx, cause); + latch.countDown(); + } + }; + + final HttpToHttp2ConnectionHandler connectionHandler = new HttpToHttp2ConnectionHandlerBuilder().connection(connection) + .frameListener( + new DelegatingDecompressorFrameListener( + connection, + new InboundHttp2ToHttpAdapterBuilder(connection).maxContentLength(maxContentLength).propagateSettings(true).build() + ) + ) + .build(); + + final HttpClientCodec sourceCodec = new HttpClientCodec(); + final Http2ClientUpgradeCodec upgradeCodec = new Http2ClientUpgradeCodec(connectionHandler); + final HttpClientUpgradeHandler upgradeHandler = new HttpClientUpgradeHandler(sourceCodec, upgradeCodec, maxContentLength); + + ch.pipeline().addLast(sourceCodec); + ch.pipeline().addLast(upgradeHandler); + ch.pipeline().addLast(new HttpContentDecompressor()); + ch.pipeline().addLast(new UpgradeRequestHandler(settingsHandler, responseHandler)); + } + + @Override + void await() { + try { + // Await for HTTP/2 settings being sent over before moving on to sending the requests + settingsHandler.awaitSettings(5, TimeUnit.SECONDS); + } catch (final Exception ex) { + throw new RuntimeException(ex); + } + } + } + + /** + * A handler that triggers the cleartext upgrade to HTTP/2 (h2c) by sending an + * initial HTTP request. + */ + private static class UpgradeRequestHandler extends ChannelInboundHandlerAdapter { + private final ChannelInboundHandler settingsHandler; + private final ChannelInboundHandler responseHandler; + + UpgradeRequestHandler(final ChannelInboundHandler settingsHandler, final ChannelInboundHandler responseHandler) { + this.settingsHandler = settingsHandler; + this.responseHandler = responseHandler; + } + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + // The first request is HTTP/2 protocol upgrade (since we support only h2c there) + final FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/"); + request.headers().add(HttpHeaderNames.HOST, "localhost"); + request.headers().add(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), "http"); + + ctx.channel().attr(AttributeKey.newInstance("upgrade")).set(true); + ctx.writeAndFlush(request); + ctx.fireChannelActive(); + + ctx.pipeline().remove(this); + ctx.pipeline().addLast(settingsHandler); + ctx.pipeline().addLast(responseHandler); + } + } + + private static class Http2SettingsHandler extends SimpleChannelInboundHandler { + private ChannelPromise promise; + + Http2SettingsHandler(ChannelPromise promise) { + this.promise = promise; + } + + /** + * Wait for this handler to be added after the upgrade to HTTP/2, and for initial preface + * handshake to complete. + */ + void awaitSettings(long timeout, TimeUnit unit) throws Exception { + if (!promise.awaitUninterruptibly(timeout, unit)) { + throw new IllegalStateException("Timed out waiting for HTTP/2 settings"); + } + if (!promise.isSuccess()) { + throw new RuntimeException(promise.cause()); + } + } + + @Override + protected void channelRead0(ChannelHandlerContext ctx, Http2Settings msg) throws Exception { + promise.setSuccess(); + ctx.pipeline().remove(this); + } + } + } diff --git a/modules/transport-netty4/src/test/java/org/opensearch/http/netty4/Netty4HttpServerPipeliningTests.java b/modules/transport-netty4/src/test/java/org/opensearch/http/netty4/Netty4HttpServerPipeliningTests.java index 029aed1f3cc89..cda66b8d828fa 100644 --- a/modules/transport-netty4/src/test/java/org/opensearch/http/netty4/Netty4HttpServerPipeliningTests.java +++ b/modules/transport-netty4/src/test/java/org/opensearch/http/netty4/Netty4HttpServerPipeliningTests.java @@ -109,7 +109,7 @@ public void testThatHttpPipeliningWorks() throws Exception { } } - try (Netty4HttpClient nettyHttpClient = new Netty4HttpClient()) { + try (Netty4HttpClient nettyHttpClient = Netty4HttpClient.http()) { Collection responses = nettyHttpClient.get(transportAddress.address(), requests.toArray(new String[] {})); try { Collection responseBodies = Netty4HttpClient.returnHttpResponseBodies(responses); @@ -163,9 +163,12 @@ private class CustomHttpChannelHandler extends Netty4HttpServerTransport.HttpCha @Override protected void initChannel(Channel ch) throws Exception { super.initChannel(ch); - ch.pipeline().replace("handler", "handler", new PossiblySlowUpstreamHandler(executorService)); } + @Override + public ChannelHandler getRequestHandler() { + return new PossiblySlowUpstreamHandler(executorService); + } } class PossiblySlowUpstreamHandler extends SimpleChannelInboundHandler { diff --git a/modules/transport-netty4/src/test/java/org/opensearch/http/netty4/Netty4HttpServerTransportTests.java b/modules/transport-netty4/src/test/java/org/opensearch/http/netty4/Netty4HttpServerTransportTests.java index ec879e538fe20..eb96f14f10c70 100644 --- a/modules/transport-netty4/src/test/java/org/opensearch/http/netty4/Netty4HttpServerTransportTests.java +++ b/modules/transport-netty4/src/test/java/org/opensearch/http/netty4/Netty4HttpServerTransportTests.java @@ -202,7 +202,7 @@ public void dispatchBadRequest(RestChannel channel, ThreadContext threadContext, ) { transport.start(); final TransportAddress remoteAddress = randomFrom(transport.boundAddress().boundAddresses()); - try (Netty4HttpClient client = new Netty4HttpClient()) { + try (Netty4HttpClient client = Netty4HttpClient.http()) { final FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/"); request.headers().set(HttpHeaderNames.EXPECT, expectation); HttpUtil.setContentLength(request, contentLength); @@ -322,7 +322,7 @@ public void dispatchBadRequest(final RestChannel channel, final ThreadContext th transport.start(); final TransportAddress remoteAddress = randomFrom(transport.boundAddress().boundAddresses()); - try (Netty4HttpClient client = new Netty4HttpClient()) { + try (Netty4HttpClient client = Netty4HttpClient.http()) { final String url = "/" + new String(new byte[maxInitialLineLength], Charset.forName("UTF-8")); final FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, url); @@ -384,7 +384,7 @@ public void dispatchBadRequest(final RestChannel channel, final ThreadContext th transport.start(); final TransportAddress remoteAddress = randomFrom(transport.boundAddress().boundAddresses()); - try (Netty4HttpClient client = new Netty4HttpClient()) { + try (Netty4HttpClient client = Netty4HttpClient.http()) { DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, url); request.headers().add(HttpHeaderNames.ACCEPT_ENCODING, randomFrom("deflate", "gzip")); long numOfHugeAllocations = getHugeAllocationCount(); @@ -454,7 +454,7 @@ public void dispatchBadRequest(final RestChannel channel, final ThreadContext th final TransportAddress remoteAddress = randomFrom(transport.boundAddress().boundAddresses()); // Test pre-flight request - try (Netty4HttpClient client = new Netty4HttpClient()) { + try (Netty4HttpClient client = Netty4HttpClient.http()) { final FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.OPTIONS, "/"); request.headers().add(CorsHandler.ORIGIN, "test-cors.org"); request.headers().add(CorsHandler.ACCESS_CONTROL_REQUEST_METHOD, "POST"); @@ -471,7 +471,7 @@ public void dispatchBadRequest(final RestChannel channel, final ThreadContext th } // Test short-circuited request - try (Netty4HttpClient client = new Netty4HttpClient()) { + try (Netty4HttpClient client = Netty4HttpClient.http()) { final FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/"); request.headers().add(CorsHandler.ORIGIN, "google.com"); From c28221e0176ad3ce782c18a2b23ea2e59ed0e0a8 Mon Sep 17 00:00:00 2001 From: Kunal Kotwani Date: Wed, 31 Aug 2022 09:41:37 -0700 Subject: [PATCH 068/187] Fix token usage for changelog helper (#4351) Signed-off-by: Kunal Kotwani Signed-off-by: Kunal Kotwani --- .github/workflows/changelog_verifier.yml | 13 ++++++++++++- CHANGELOG.md | 1 + 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/changelog_verifier.yml b/.github/workflows/changelog_verifier.yml index ac0c0ec4d7297..fb4f8ea3f8ecc 100644 --- a/.github/workflows/changelog_verifier.yml +++ b/.github/workflows/changelog_verifier.yml @@ -7,10 +7,21 @@ jobs: # Enforces the update of a changelog file on every pull request verify-changelog: runs-on: ubuntu-latest + permissions: + pull-requests: write + contents: write steps: + - name: GitHub App token + id: github_app_token + uses: tibdex/github-app-token@v1.5.0 + with: + app_id: ${{ secrets.APP_ID }} + private_key: ${{ secrets.APP_PRIVATE_KEY }} + installation_id: 22958780 + - uses: actions/checkout@v3 with: - token: ${{ secrets.GITHUB_TOKEN }} + token: ${{ steps.github_app_token.outputs.token }} ref: ${{ github.event.pull_request.head.sha }} - uses: dangoslen/dependabot-changelog-helper@v1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e7fa8b5547f0..c9b8b1041bd9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Do not fail replica shard due to primary closure ([#4133](https://github.com/opensearch-project/OpenSearch/pull/4133)) - Add timeout on Mockito.verify to reduce flakyness in testReplicationOnDone test([#4314](https://github.com/opensearch-project/OpenSearch/pull/4314)) - Commit workflow for dependabot changelog helper ([#4331](https://github.com/opensearch-project/OpenSearch/pull/4331)) +- Token usage for dependabot changelog helper ([#4351](https://github.com/opensearch-project/OpenSearch/pull/4351)) ### Security - CVE-2022-25857 org.yaml:snakeyaml DOS vulnerability ([#4341](https://github.com/opensearch-project/OpenSearch/pull/4341)) From 100120a440c63ac8ebe4cf4bad51de29ce54ebf9 Mon Sep 17 00:00:00 2001 From: Kunal Kotwani Date: Wed, 31 Aug 2022 11:55:49 -0700 Subject: [PATCH 069/187] Revert "Fix token usage for changelog helper (#4351)" (#4361) This reverts commit c28221e0176ad3ce782c18a2b23ea2e59ed0e0a8. Signed-off-by: Kunal Kotwani Signed-off-by: Kunal Kotwani --- .github/workflows/changelog_verifier.yml | 13 +------------ CHANGELOG.md | 1 - 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/.github/workflows/changelog_verifier.yml b/.github/workflows/changelog_verifier.yml index fb4f8ea3f8ecc..ac0c0ec4d7297 100644 --- a/.github/workflows/changelog_verifier.yml +++ b/.github/workflows/changelog_verifier.yml @@ -7,21 +7,10 @@ jobs: # Enforces the update of a changelog file on every pull request verify-changelog: runs-on: ubuntu-latest - permissions: - pull-requests: write - contents: write steps: - - name: GitHub App token - id: github_app_token - uses: tibdex/github-app-token@v1.5.0 - with: - app_id: ${{ secrets.APP_ID }} - private_key: ${{ secrets.APP_PRIVATE_KEY }} - installation_id: 22958780 - - uses: actions/checkout@v3 with: - token: ${{ steps.github_app_token.outputs.token }} + token: ${{ secrets.GITHUB_TOKEN }} ref: ${{ github.event.pull_request.head.sha }} - uses: dangoslen/dependabot-changelog-helper@v1 diff --git a/CHANGELOG.md b/CHANGELOG.md index c9b8b1041bd9e..8e7fa8b5547f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,7 +27,6 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Do not fail replica shard due to primary closure ([#4133](https://github.com/opensearch-project/OpenSearch/pull/4133)) - Add timeout on Mockito.verify to reduce flakyness in testReplicationOnDone test([#4314](https://github.com/opensearch-project/OpenSearch/pull/4314)) - Commit workflow for dependabot changelog helper ([#4331](https://github.com/opensearch-project/OpenSearch/pull/4331)) -- Token usage for dependabot changelog helper ([#4351](https://github.com/opensearch-project/OpenSearch/pull/4351)) ### Security - CVE-2022-25857 org.yaml:snakeyaml DOS vulnerability ([#4341](https://github.com/opensearch-project/OpenSearch/pull/4341)) From 19d1a2b027fef8b981560969bf428476d700bd07 Mon Sep 17 00:00:00 2001 From: Marc Handalian Date: Wed, 31 Aug 2022 13:12:24 -0700 Subject: [PATCH 070/187] Segment Replication - Implement segment replication event cancellation. (#4225) * Segment Replication. Fix Cancellation of replication events. This PR updates segment replication paths to correctly cancel replication events on the primary and replica. In the source service, any ongoing event for a primary that is sending to a replica that shuts down or is promoted as a new primary are cancelled. In the target service, any ongoing event for a replica that is promoted as a new primary or is fetching from a primary that shuts down. It wires up SegmentReplicationSourceService as an IndexEventListener so that it can respond to events and cancel any ongoing transfer state. This change also includes some test cleanup for segment replication to rely on actual components over mocks. Signed-off-by: Marc Handalian Fix to not start/stop SegmentReplicationSourceService as a lifecycle component with feature flag off. Signed-off-by: Marc Handalian Update logic to properly mark SegmentReplicationTarget as cancelled when cancel initiated by primary. Signed-off-by: Marc Handalian Minor updates from self review. Signed-off-by: Marc Handalian * Add missing changelog entry. Signed-off-by: Marc Handalian Signed-off-by: Marc Handalian --- CHANGELOG.md | 1 + .../cluster/IndicesClusterStateService.java | 5 + .../OngoingSegmentReplications.java | 22 +- .../PrimaryShardReplicationSource.java | 6 + .../replication/SegmentReplicationSource.java | 6 + .../SegmentReplicationSourceHandler.java | 11 + .../SegmentReplicationSourceService.java | 44 ++- .../replication/SegmentReplicationState.java | 15 +- .../replication/SegmentReplicationTarget.java | 31 +- .../SegmentReplicationTargetService.java | 53 +++- .../main/java/org/opensearch/node/Node.java | 7 + .../SegmentReplicationIndexShardTests.java | 275 ++++++++++++++++++ ...ClusterStateServiceRandomUpdatesTests.java | 2 + .../OngoingSegmentReplicationsTests.java | 49 ++++ .../PrimaryShardReplicationSourceTests.java | 37 +++ .../SegmentReplicationSourceHandlerTests.java | 46 +++ .../SegmentReplicationTargetServiceTests.java | 200 +++++-------- .../snapshots/SnapshotResiliencyTests.java | 2 + .../index/shard/IndexShardTestCase.java | 118 +++++--- 19 files changed, 737 insertions(+), 193 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e7fa8b5547f0..4d07052d55ff0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Do not fail replica shard due to primary closure ([#4133](https://github.com/opensearch-project/OpenSearch/pull/4133)) - Add timeout on Mockito.verify to reduce flakyness in testReplicationOnDone test([#4314](https://github.com/opensearch-project/OpenSearch/pull/4314)) - Commit workflow for dependabot changelog helper ([#4331](https://github.com/opensearch-project/OpenSearch/pull/4331)) +- Fixed cancellation of segment replication events ([#4225](https://github.com/opensearch-project/OpenSearch/pull/4225)) ### Security - CVE-2022-25857 org.yaml:snakeyaml DOS vulnerability ([#4341](https://github.com/opensearch-project/OpenSearch/pull/4341)) diff --git a/server/src/main/java/org/opensearch/indices/cluster/IndicesClusterStateService.java b/server/src/main/java/org/opensearch/indices/cluster/IndicesClusterStateService.java index 8884ef2cddd0a..15a9bf9e4c492 100644 --- a/server/src/main/java/org/opensearch/indices/cluster/IndicesClusterStateService.java +++ b/server/src/main/java/org/opensearch/indices/cluster/IndicesClusterStateService.java @@ -81,6 +81,7 @@ import org.opensearch.indices.recovery.PeerRecoveryTargetService; import org.opensearch.indices.recovery.RecoveryListener; import org.opensearch.indices.recovery.RecoveryState; +import org.opensearch.indices.replication.SegmentReplicationSourceService; import org.opensearch.indices.replication.SegmentReplicationTargetService; import org.opensearch.indices.replication.checkpoint.SegmentReplicationCheckpointPublisher; import org.opensearch.indices.replication.common.ReplicationState; @@ -152,6 +153,7 @@ public IndicesClusterStateService( final ThreadPool threadPool, final PeerRecoveryTargetService recoveryTargetService, final SegmentReplicationTargetService segmentReplicationTargetService, + final SegmentReplicationSourceService segmentReplicationSourceService, final ShardStateAction shardStateAction, final NodeMappingRefreshAction nodeMappingRefreshAction, final RepositoriesService repositoriesService, @@ -170,6 +172,7 @@ public IndicesClusterStateService( threadPool, checkpointPublisher, segmentReplicationTargetService, + segmentReplicationSourceService, recoveryTargetService, shardStateAction, nodeMappingRefreshAction, @@ -191,6 +194,7 @@ public IndicesClusterStateService( final ThreadPool threadPool, final SegmentReplicationCheckpointPublisher checkpointPublisher, final SegmentReplicationTargetService segmentReplicationTargetService, + final SegmentReplicationSourceService segmentReplicationSourceService, final PeerRecoveryTargetService recoveryTargetService, final ShardStateAction shardStateAction, final NodeMappingRefreshAction nodeMappingRefreshAction, @@ -211,6 +215,7 @@ public IndicesClusterStateService( // if segrep feature flag is not enabled, don't wire the target serivce as an IndexEventListener. if (FeatureFlags.isEnabled(FeatureFlags.REPLICATION_TYPE)) { indexEventListeners.add(segmentReplicationTargetService); + indexEventListeners.add(segmentReplicationSourceService); } this.builtInIndexListener = Collections.unmodifiableList(indexEventListeners); this.indicesService = indicesService; diff --git a/server/src/main/java/org/opensearch/indices/replication/OngoingSegmentReplications.java b/server/src/main/java/org/opensearch/indices/replication/OngoingSegmentReplications.java index dfebe5f7cabf2..828aa29192fe3 100644 --- a/server/src/main/java/org/opensearch/indices/replication/OngoingSegmentReplications.java +++ b/server/src/main/java/org/opensearch/indices/replication/OngoingSegmentReplications.java @@ -37,7 +37,6 @@ * @opensearch.internal */ class OngoingSegmentReplications { - private final RecoverySettings recoverySettings; private final IndicesService indicesService; private final Map copyStateMap; @@ -161,6 +160,20 @@ synchronized void cancel(IndexShard shard, String reason) { cancelHandlers(handler -> handler.getCopyState().getShard().shardId().equals(shard.shardId()), reason); } + /** + * Cancel all Replication events for the given allocation ID, intended to be called when a primary is shutting down. + * + * @param allocationId {@link String} - Allocation ID. + * @param reason {@link String} - Reason for the cancel + */ + synchronized void cancel(String allocationId, String reason) { + final SegmentReplicationSourceHandler handler = allocationIdToHandlers.remove(allocationId); + if (handler != null) { + handler.cancel(reason); + removeCopyState(handler.getCopyState()); + } + } + /** * Cancel any ongoing replications for a given {@link DiscoveryNode} * @@ -168,7 +181,6 @@ synchronized void cancel(IndexShard shard, String reason) { */ void cancelReplication(DiscoveryNode node) { cancelHandlers(handler -> handler.getTargetNode().equals(node), "Node left"); - } /** @@ -243,11 +255,7 @@ private void cancelHandlers(Predicate p .map(SegmentReplicationSourceHandler::getAllocationId) .collect(Collectors.toList()); for (String allocationId : allocationIds) { - final SegmentReplicationSourceHandler handler = allocationIdToHandlers.remove(allocationId); - if (handler != null) { - handler.cancel(reason); - removeCopyState(handler.getCopyState()); - } + cancel(allocationId, reason); } } } diff --git a/server/src/main/java/org/opensearch/indices/replication/PrimaryShardReplicationSource.java b/server/src/main/java/org/opensearch/indices/replication/PrimaryShardReplicationSource.java index 08dc0b97b31d5..aa0b5416dd0ff 100644 --- a/server/src/main/java/org/opensearch/indices/replication/PrimaryShardReplicationSource.java +++ b/server/src/main/java/org/opensearch/indices/replication/PrimaryShardReplicationSource.java @@ -87,4 +87,10 @@ public void getSegmentFiles( ); transportClient.executeRetryableAction(GET_SEGMENT_FILES, request, responseListener, reader); } + + @Override + public void cancel() { + transportClient.cancel(); + } + } diff --git a/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationSource.java b/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationSource.java index 8628a266ea7d0..b2e7487fff4b2 100644 --- a/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationSource.java +++ b/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationSource.java @@ -9,6 +9,7 @@ package org.opensearch.indices.replication; import org.opensearch.action.ActionListener; +import org.opensearch.common.util.CancellableThreads.ExecutionCancelledException; import org.opensearch.index.store.Store; import org.opensearch.index.store.StoreFileMetadata; import org.opensearch.indices.replication.checkpoint.ReplicationCheckpoint; @@ -47,4 +48,9 @@ void getSegmentFiles( Store store, ActionListener listener ); + + /** + * Cancel any ongoing requests, should resolve any ongoing listeners with onFailure with a {@link ExecutionCancelledException}. + */ + default void cancel() {} } diff --git a/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationSourceHandler.java b/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationSourceHandler.java index 2d21653c1924c..022d90b41d8ee 100644 --- a/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationSourceHandler.java +++ b/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationSourceHandler.java @@ -113,6 +113,16 @@ public synchronized void sendFiles(GetSegmentFilesRequest request, ActionListene final Closeable releaseResources = () -> IOUtils.close(resources); try { timer.start(); + cancellableThreads.setOnCancel((reason, beforeCancelEx) -> { + final RuntimeException e = new CancellableThreads.ExecutionCancelledException( + "replication was canceled reason [" + reason + "]" + ); + if (beforeCancelEx != null) { + e.addSuppressed(beforeCancelEx); + } + IOUtils.closeWhileHandlingException(releaseResources, () -> future.onFailure(e)); + throw e; + }); final Consumer onFailure = e -> { assert Transports.assertNotTransportThread(SegmentReplicationSourceHandler.this + "[onFailure]"); IOUtils.closeWhileHandlingException(releaseResources, () -> future.onFailure(e)); @@ -153,6 +163,7 @@ public synchronized void sendFiles(GetSegmentFilesRequest request, ActionListene final MultiChunkTransfer transfer = segmentFileTransferHandler .createTransfer(shard.store(), storeFileMetadata, () -> 0, sendFileStep); resources.add(transfer); + cancellableThreads.checkForCancel(); transfer.start(); sendFileStep.whenComplete(r -> { diff --git a/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationSourceService.java b/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationSourceService.java index 0cee731fde2cb..db3f87201b774 100644 --- a/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationSourceService.java +++ b/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationSourceService.java @@ -15,6 +15,7 @@ import org.opensearch.cluster.ClusterChangedEvent; import org.opensearch.cluster.ClusterStateListener; import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Nullable; import org.opensearch.common.component.AbstractLifecycleComponent; @@ -42,7 +43,25 @@ * * @opensearch.internal */ -public final class SegmentReplicationSourceService extends AbstractLifecycleComponent implements ClusterStateListener, IndexEventListener { +public class SegmentReplicationSourceService extends AbstractLifecycleComponent implements ClusterStateListener, IndexEventListener { + + // Empty Implementation, only required while Segment Replication is under feature flag. + public static final SegmentReplicationSourceService NO_OP = new SegmentReplicationSourceService() { + @Override + public void clusterChanged(ClusterChangedEvent event) { + // NoOp; + } + + @Override + public void beforeIndexShardClosed(ShardId shardId, IndexShard indexShard, Settings indexSettings) { + // NoOp; + } + + @Override + public void shardRoutingChanged(IndexShard indexShard, @Nullable ShardRouting oldRouting, ShardRouting newRouting) { + // NoOp; + } + }; private static final Logger logger = LogManager.getLogger(SegmentReplicationSourceService.class); private final RecoverySettings recoverySettings; @@ -62,6 +81,14 @@ public static class Actions { private final OngoingSegmentReplications ongoingSegmentReplications; + // Used only for empty implementation. + private SegmentReplicationSourceService() { + recoverySettings = null; + ongoingSegmentReplications = null; + transportService = null; + indicesService = null; + } + public SegmentReplicationSourceService( IndicesService indicesService, TransportService transportService, @@ -163,10 +190,25 @@ protected void doClose() throws IOException { } + /** + * + * Cancels any replications on this node to a replica shard that is about to be closed. + */ @Override public void beforeIndexShardClosed(ShardId shardId, @Nullable IndexShard indexShard, Settings indexSettings) { if (indexShard != null) { ongoingSegmentReplications.cancel(indexShard, "shard is closed"); } } + + /** + * Cancels any replications on this node to a replica that has been promoted as primary. + */ + @Override + public void shardRoutingChanged(IndexShard indexShard, @Nullable ShardRouting oldRouting, ShardRouting newRouting) { + if (indexShard != null && oldRouting.primary() == false && newRouting.primary()) { + ongoingSegmentReplications.cancel(indexShard.routingEntry().allocationId().getId(), "Relocating primary shard."); + } + } + } diff --git a/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationState.java b/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationState.java index f865ba1332186..2e2e6df007c5c 100644 --- a/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationState.java +++ b/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationState.java @@ -35,7 +35,8 @@ public enum Stage { GET_CHECKPOINT_INFO((byte) 3), FILE_DIFF((byte) 4), GET_FILES((byte) 5), - FINALIZE_REPLICATION((byte) 6); + FINALIZE_REPLICATION((byte) 6), + CANCELLED((byte) 7); private static final Stage[] STAGES = new Stage[Stage.values().length]; @@ -118,6 +119,10 @@ protected void validateAndSetStage(Stage expected, Stage next) { "can't move replication to stage [" + next + "]. current stage: [" + stage + "] (expected [" + expected + "])" ); } + stopTimersAndSetStage(next); + } + + private void stopTimersAndSetStage(Stage next) { // save the timing data for the current step stageTimer.stop(); timingData.add(new Tuple<>(stage.name(), stageTimer.time())); @@ -155,6 +160,14 @@ public void setStage(Stage stage) { overallTimer.stop(); timingData.add(new Tuple<>("OVERALL", overallTimer.time())); break; + case CANCELLED: + if (this.stage == Stage.DONE) { + throw new IllegalStateException("can't move replication to Cancelled state from Done."); + } + stopTimersAndSetStage(Stage.CANCELLED); + overallTimer.stop(); + timingData.add(new Tuple<>("OVERALL", overallTimer.time())); + break; default: throw new IllegalArgumentException("unknown SegmentReplicationState.Stage [" + stage + "]"); } diff --git a/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationTarget.java b/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationTarget.java index a658ffc09d590..d1d6104a416ca 100644 --- a/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationTarget.java +++ b/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationTarget.java @@ -17,6 +17,7 @@ import org.apache.lucene.store.ByteBuffersDataInput; import org.apache.lucene.store.ByteBuffersIndexInput; import org.apache.lucene.store.ChecksumIndexInput; +import org.opensearch.ExceptionsHelper; import org.opensearch.OpenSearchException; import org.opensearch.action.ActionListener; import org.opensearch.action.StepListener; @@ -103,7 +104,15 @@ public String description() { @Override public void notifyListener(OpenSearchException e, boolean sendShardFailure) { - listener.onFailure(state(), e, sendShardFailure); + // Cancellations still are passed to our SegmentReplicationListner as failures, if we have failed because of cancellation + // update the stage. + final Throwable cancelledException = ExceptionsHelper.unwrap(e, CancellableThreads.ExecutionCancelledException.class); + if (cancelledException != null) { + state.setStage(SegmentReplicationState.Stage.CANCELLED); + listener.onFailure(state(), (CancellableThreads.ExecutionCancelledException) cancelledException, sendShardFailure); + } else { + listener.onFailure(state(), e, sendShardFailure); + } } @Override @@ -134,6 +143,14 @@ public void writeFileChunk( * @param listener {@link ActionListener} listener. */ public void startReplication(ActionListener listener) { + cancellableThreads.setOnCancel((reason, beforeCancelEx) -> { + // This method only executes when cancellation is triggered by this node and caught by a call to checkForCancel, + // SegmentReplicationSource does not share CancellableThreads. + final CancellableThreads.ExecutionCancelledException executionCancelledException = + new CancellableThreads.ExecutionCancelledException("replication was canceled reason [" + reason + "]"); + notifyListener(executionCancelledException, false); + throw executionCancelledException; + }); state.setStage(SegmentReplicationState.Stage.REPLICATING); final StepListener checkpointInfoListener = new StepListener<>(); final StepListener getFilesListener = new StepListener<>(); @@ -141,6 +158,7 @@ public void startReplication(ActionListener listener) { logger.trace("[shardId {}] Replica starting replication [id {}]", shardId().getId(), getId()); // Get list of files to copy from this checkpoint. + cancellableThreads.checkForCancel(); state.setStage(SegmentReplicationState.Stage.GET_CHECKPOINT_INFO); source.getCheckpointMetadata(getId(), checkpoint, checkpointInfoListener); @@ -154,6 +172,7 @@ public void startReplication(ActionListener listener) { private void getFiles(CheckpointInfoResponse checkpointInfo, StepListener getFilesListener) throws IOException { + cancellableThreads.checkForCancel(); state.setStage(SegmentReplicationState.Stage.FILE_DIFF); final Store.MetadataSnapshot snapshot = checkpointInfo.getSnapshot(); Store.MetadataSnapshot localMetadata = getMetadataSnapshot(); @@ -188,12 +207,14 @@ private void getFiles(CheckpointInfoResponse checkpointInfo, StepListener listener) { - state.setStage(SegmentReplicationState.Stage.FINALIZE_REPLICATION); ActionListener.completeWith(listener, () -> { + cancellableThreads.checkForCancel(); + state.setStage(SegmentReplicationState.Stage.FINALIZE_REPLICATION); multiFileWriter.renameAllTempFiles(); final Store store = store(); store.incRef(); @@ -261,4 +282,10 @@ Store.MetadataSnapshot getMetadataSnapshot() throws IOException { } return store.getMetadata(indexShard.getSegmentInfosSnapshot().get()); } + + @Override + protected void onCancel(String reason) { + cancellableThreads.cancel(reason); + source.cancel(); + } } diff --git a/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationTargetService.java b/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationTargetService.java index a79ce195ad83b..9e6b66dc4d7d6 100644 --- a/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationTargetService.java +++ b/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationTargetService.java @@ -11,10 +11,13 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; +import org.opensearch.ExceptionsHelper; import org.opensearch.OpenSearchException; import org.opensearch.action.ActionListener; +import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.common.Nullable; import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.CancellableThreads; import org.opensearch.index.shard.IndexEventListener; import org.opensearch.index.shard.IndexShard; import org.opensearch.index.shard.ShardId; @@ -64,6 +67,11 @@ public void beforeIndexShardClosed(ShardId shardId, IndexShard indexShard, Setti public synchronized void onNewCheckpoint(ReplicationCheckpoint receivedCheckpoint, IndexShard replicaShard) { // noOp; } + + @Override + public void shardRoutingChanged(IndexShard indexShard, @Nullable ShardRouting oldRouting, ShardRouting newRouting) { + // noOp; + } }; // Used only for empty implementation. @@ -74,6 +82,10 @@ private SegmentReplicationTargetService() { sourceFactory = null; } + public ReplicationRef get(long replicationId) { + return onGoingReplications.get(replicationId); + } + /** * The internal actions * @@ -102,6 +114,9 @@ public SegmentReplicationTargetService( ); } + /** + * Cancel any replications on this node for a replica that is about to be closed. + */ @Override public void beforeIndexShardClosed(ShardId shardId, @Nullable IndexShard indexShard, Settings indexSettings) { if (indexShard != null) { @@ -109,11 +124,22 @@ public void beforeIndexShardClosed(ShardId shardId, @Nullable IndexShard indexSh } } + /** + * Cancel any replications on this node for a replica that has just been promoted as the new primary. + */ + @Override + public void shardRoutingChanged(IndexShard indexShard, @Nullable ShardRouting oldRouting, ShardRouting newRouting) { + if (oldRouting != null && oldRouting.primary() == false && newRouting.primary()) { + onGoingReplications.cancelForShard(indexShard.shardId(), "shard has been promoted to primary"); + } + } + /** * Invoked when a new checkpoint is received from a primary shard. * It checks if a new checkpoint should be processed or not and starts replication if needed. - * @param receivedCheckpoint received checkpoint that is checked for processing - * @param replicaShard replica shard on which checkpoint is received + * + * @param receivedCheckpoint received checkpoint that is checked for processing + * @param replicaShard replica shard on which checkpoint is received */ public synchronized void onNewCheckpoint(final ReplicationCheckpoint receivedCheckpoint, final IndexShard replicaShard) { logger.trace(() -> new ParameterizedMessage("Replica received new replication checkpoint from primary [{}]", receivedCheckpoint)); @@ -180,12 +206,19 @@ public void onReplicationFailure(SegmentReplicationState state, OpenSearchExcept } } - public void startReplication( + public SegmentReplicationTarget startReplication( final ReplicationCheckpoint checkpoint, final IndexShard indexShard, final SegmentReplicationListener listener ) { - startReplication(new SegmentReplicationTarget(checkpoint, indexShard, sourceFactory.get(indexShard), listener)); + final SegmentReplicationTarget target = new SegmentReplicationTarget( + checkpoint, + indexShard, + sourceFactory.get(indexShard), + listener + ); + startReplication(target); + return target; } // pkg-private for integration tests @@ -248,7 +281,17 @@ public void onResponse(Void o) { @Override public void onFailure(Exception e) { - onGoingReplications.fail(replicationId, new OpenSearchException("Segment Replication failed", e), true); + Throwable cause = ExceptionsHelper.unwrapCause(e); + if (cause instanceof CancellableThreads.ExecutionCancelledException) { + if (onGoingReplications.getTarget(replicationId) != null) { + // if the target still exists in our collection, the primary initiated the cancellation, fail the replication + // but do not fail the shard. Cancellations initiated by this node from Index events will be removed with + // onGoingReplications.cancel and not appear in the collection when this listener resolves. + onGoingReplications.fail(replicationId, (CancellableThreads.ExecutionCancelledException) cause, false); + } + } else { + onGoingReplications.fail(replicationId, new OpenSearchException("Segment Replication failed", e), true); + } } }); } diff --git a/server/src/main/java/org/opensearch/node/Node.java b/server/src/main/java/org/opensearch/node/Node.java index 3f4eadc52fd2a..92e9815313fa0 100644 --- a/server/src/main/java/org/opensearch/node/Node.java +++ b/server/src/main/java/org/opensearch/node/Node.java @@ -969,6 +969,7 @@ protected Node( .toInstance(new SegmentReplicationSourceService(indicesService, transportService, recoverySettings)); } else { b.bind(SegmentReplicationTargetService.class).toInstance(SegmentReplicationTargetService.NO_OP); + b.bind(SegmentReplicationSourceService.class).toInstance(SegmentReplicationSourceService.NO_OP); } } b.bind(HttpServerTransport.class).toInstance(httpServerTransport); @@ -1112,6 +1113,9 @@ public Node start() throws NodeValidationException { assert transportService.getLocalNode().equals(localNodeFactory.getNode()) : "transportService has a different local node than the factory provided"; injector.getInstance(PeerRecoverySourceService.class).start(); + if (FeatureFlags.isEnabled(REPLICATION_TYPE)) { + injector.getInstance(SegmentReplicationSourceService.class).start(); + } // Load (and maybe upgrade) the metadata stored on disk final GatewayMetaState gatewayMetaState = injector.getInstance(GatewayMetaState.class); @@ -1287,6 +1291,9 @@ public synchronized void close() throws IOException { // close filter/fielddata caches after indices toClose.add(injector.getInstance(IndicesStore.class)); toClose.add(injector.getInstance(PeerRecoverySourceService.class)); + if (FeatureFlags.isEnabled(REPLICATION_TYPE)) { + toClose.add(injector.getInstance(SegmentReplicationSourceService.class)); + } toClose.add(() -> stopWatch.stop().start("cluster")); toClose.add(injector.getInstance(ClusterService.class)); toClose.add(() -> stopWatch.stop().start("node_connections_service")); diff --git a/server/src/test/java/org/opensearch/index/shard/SegmentReplicationIndexShardTests.java b/server/src/test/java/org/opensearch/index/shard/SegmentReplicationIndexShardTests.java index 23371a39871c7..88a3bdad53d0c 100644 --- a/server/src/test/java/org/opensearch/index/shard/SegmentReplicationIndexShardTests.java +++ b/server/src/test/java/org/opensearch/index/shard/SegmentReplicationIndexShardTests.java @@ -8,11 +8,18 @@ package org.opensearch.index.shard; +import org.junit.Assert; +import org.opensearch.OpenSearchException; +import org.opensearch.action.ActionListener; import org.opensearch.action.delete.DeleteRequest; import org.opensearch.action.index.IndexRequest; import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.cluster.routing.ShardRouting; +import org.opensearch.common.lease.Releasable; +import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; +import org.opensearch.common.util.CancellableThreads; import org.opensearch.common.xcontent.XContentType; import org.opensearch.index.IndexSettings; import org.opensearch.index.engine.DocIdSeqNoAndSource; @@ -21,12 +28,28 @@ import org.opensearch.index.engine.NRTReplicationEngineFactory; import org.opensearch.index.mapper.MapperService; import org.opensearch.index.replication.OpenSearchIndexLevelReplicationTestCase; +import org.opensearch.index.store.Store; +import org.opensearch.index.store.StoreFileMetadata; +import org.opensearch.indices.recovery.RecoverySettings; +import org.opensearch.indices.replication.CheckpointInfoResponse; +import org.opensearch.indices.replication.GetSegmentFilesResponse; +import org.opensearch.indices.replication.SegmentReplicationSource; +import org.opensearch.indices.replication.SegmentReplicationSourceFactory; +import org.opensearch.indices.replication.SegmentReplicationState; +import org.opensearch.indices.replication.SegmentReplicationTarget; +import org.opensearch.indices.replication.SegmentReplicationTargetService; import org.opensearch.indices.replication.checkpoint.SegmentReplicationCheckpointPublisher; import org.opensearch.indices.replication.checkpoint.ReplicationCheckpoint; +import org.opensearch.indices.replication.common.CopyState; import org.opensearch.indices.replication.common.ReplicationType; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportService; import java.io.IOException; +import java.util.Collections; import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import static java.util.Arrays.asList; import static org.hamcrest.Matchers.equalTo; @@ -34,6 +57,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; public class SegmentReplicationIndexShardTests extends OpenSearchIndexLevelReplicationTestCase { @@ -241,6 +265,213 @@ public void testNRTReplicaPromotedAsPrimary() throws Exception { } } + public void testReplicaPromotedWhileReplicating() throws Exception { + try (ReplicationGroup shards = createGroup(1, settings, new NRTReplicationEngineFactory())) { + shards.startAll(); + final IndexShard oldPrimary = shards.getPrimary(); + final IndexShard nextPrimary = shards.getReplicas().get(0); + + final int numDocs = shards.indexDocs(randomInt(10)); + oldPrimary.refresh("Test"); + shards.syncGlobalCheckpoint(); + + final SegmentReplicationSourceFactory sourceFactory = mock(SegmentReplicationSourceFactory.class); + final SegmentReplicationTargetService targetService = newTargetService(sourceFactory); + SegmentReplicationSource source = new SegmentReplicationSource() { + @Override + public void getCheckpointMetadata( + long replicationId, + ReplicationCheckpoint checkpoint, + ActionListener listener + ) { + resolveCheckpointInfoResponseListener(listener, oldPrimary); + ShardRouting oldRouting = nextPrimary.shardRouting; + try { + shards.promoteReplicaToPrimary(nextPrimary); + } catch (IOException e) { + Assert.fail("Promotion should not fail"); + } + targetService.shardRoutingChanged(nextPrimary, oldRouting, nextPrimary.shardRouting); + } + + @Override + public void getSegmentFiles( + long replicationId, + ReplicationCheckpoint checkpoint, + List filesToFetch, + Store store, + ActionListener listener + ) { + listener.onResponse(new GetSegmentFilesResponse(Collections.emptyList())); + } + }; + when(sourceFactory.get(any())).thenReturn(source); + startReplicationAndAssertCancellation(nextPrimary, targetService); + // wait for replica to finish being promoted, and assert doc counts. + final CountDownLatch latch = new CountDownLatch(1); + nextPrimary.acquirePrimaryOperationPermit(new ActionListener<>() { + @Override + public void onResponse(Releasable releasable) { + latch.countDown(); + } + + @Override + public void onFailure(Exception e) { + throw new AssertionError(e); + } + }, ThreadPool.Names.GENERIC, ""); + latch.await(); + assertEquals(nextPrimary.getEngine().getClass(), InternalEngine.class); + nextPrimary.refresh("test"); + + oldPrimary.close("demoted", false); + oldPrimary.store().close(); + IndexShard newReplica = shards.addReplicaWithExistingPath(oldPrimary.shardPath(), oldPrimary.routingEntry().currentNodeId()); + shards.recoverReplica(newReplica); + + assertDocCount(nextPrimary, numDocs); + assertDocCount(newReplica, numDocs); + + nextPrimary.refresh("test"); + replicateSegments(nextPrimary, shards.getReplicas()); + final List docsAfterRecovery = getDocIdAndSeqNos(shards.getPrimary()); + for (IndexShard shard : shards.getReplicas()) { + assertThat(shard.routingEntry().toString(), getDocIdAndSeqNos(shard), equalTo(docsAfterRecovery)); + } + } + } + + public void testReplicaClosesWhileReplicating_AfterGetCheckpoint() throws Exception { + try (ReplicationGroup shards = createGroup(1, settings, new NRTReplicationEngineFactory())) { + shards.startAll(); + IndexShard primary = shards.getPrimary(); + final IndexShard replica = shards.getReplicas().get(0); + + final int numDocs = shards.indexDocs(randomInt(10)); + primary.refresh("Test"); + + final SegmentReplicationSourceFactory sourceFactory = mock(SegmentReplicationSourceFactory.class); + final SegmentReplicationTargetService targetService = newTargetService(sourceFactory); + SegmentReplicationSource source = new SegmentReplicationSource() { + @Override + public void getCheckpointMetadata( + long replicationId, + ReplicationCheckpoint checkpoint, + ActionListener listener + ) { + // trigger a cancellation by closing the replica. + targetService.beforeIndexShardClosed(replica.shardId, replica, Settings.EMPTY); + resolveCheckpointInfoResponseListener(listener, primary); + } + + @Override + public void getSegmentFiles( + long replicationId, + ReplicationCheckpoint checkpoint, + List filesToFetch, + Store store, + ActionListener listener + ) { + Assert.fail("Should not be reached"); + } + }; + when(sourceFactory.get(any())).thenReturn(source); + startReplicationAndAssertCancellation(replica, targetService); + + shards.removeReplica(replica); + closeShards(replica); + } + } + + public void testReplicaClosesWhileReplicating_AfterGetSegmentFiles() throws Exception { + try (ReplicationGroup shards = createGroup(1, settings, new NRTReplicationEngineFactory())) { + shards.startAll(); + IndexShard primary = shards.getPrimary(); + final IndexShard replica = shards.getReplicas().get(0); + + final int numDocs = shards.indexDocs(randomInt(10)); + primary.refresh("Test"); + + final SegmentReplicationSourceFactory sourceFactory = mock(SegmentReplicationSourceFactory.class); + final SegmentReplicationTargetService targetService = newTargetService(sourceFactory); + SegmentReplicationSource source = new SegmentReplicationSource() { + @Override + public void getCheckpointMetadata( + long replicationId, + ReplicationCheckpoint checkpoint, + ActionListener listener + ) { + resolveCheckpointInfoResponseListener(listener, primary); + } + + @Override + public void getSegmentFiles( + long replicationId, + ReplicationCheckpoint checkpoint, + List filesToFetch, + Store store, + ActionListener listener + ) { + // randomly resolve the listener, indicating the source has resolved. + listener.onResponse(new GetSegmentFilesResponse(Collections.emptyList())); + targetService.beforeIndexShardClosed(replica.shardId, replica, Settings.EMPTY); + } + }; + when(sourceFactory.get(any())).thenReturn(source); + startReplicationAndAssertCancellation(replica, targetService); + + shards.removeReplica(replica); + closeShards(replica); + } + } + + public void testPrimaryCancelsExecution() throws Exception { + try (ReplicationGroup shards = createGroup(1, settings, new NRTReplicationEngineFactory())) { + shards.startAll(); + IndexShard primary = shards.getPrimary(); + final IndexShard replica = shards.getReplicas().get(0); + + final int numDocs = shards.indexDocs(randomInt(10)); + primary.refresh("Test"); + + final SegmentReplicationSourceFactory sourceFactory = mock(SegmentReplicationSourceFactory.class); + final SegmentReplicationTargetService targetService = newTargetService(sourceFactory); + SegmentReplicationSource source = new SegmentReplicationSource() { + @Override + public void getCheckpointMetadata( + long replicationId, + ReplicationCheckpoint checkpoint, + ActionListener listener + ) { + listener.onFailure(new CancellableThreads.ExecutionCancelledException("Cancelled")); + } + + @Override + public void getSegmentFiles( + long replicationId, + ReplicationCheckpoint checkpoint, + List filesToFetch, + Store store, + ActionListener listener + ) {} + }; + when(sourceFactory.get(any())).thenReturn(source); + startReplicationAndAssertCancellation(replica, targetService); + + shards.removeReplica(replica); + closeShards(replica); + } + } + + private SegmentReplicationTargetService newTargetService(SegmentReplicationSourceFactory sourceFactory) { + return new SegmentReplicationTargetService( + threadPool, + new RecoverySettings(Settings.EMPTY, new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS)), + mock(TransportService.class), + sourceFactory + ); + } + /** * Assert persisted and searchable doc counts. This method should not be used while docs are concurrently indexed because * it asserts point in time seqNos are relative to the doc counts. @@ -253,4 +484,48 @@ private void assertDocCounts(IndexShard indexShard, int expectedPersistedDocCoun // processed cp should be 1 less than our searchable doc count. assertEquals(expectedSearchableDocCount - 1, indexShard.getProcessedLocalCheckpoint()); } + + private void resolveCheckpointInfoResponseListener(ActionListener listener, IndexShard primary) { + try { + final CopyState copyState = new CopyState(ReplicationCheckpoint.empty(primary.shardId), primary); + listener.onResponse( + new CheckpointInfoResponse( + copyState.getCheckpoint(), + copyState.getMetadataSnapshot(), + copyState.getInfosBytes(), + copyState.getPendingDeleteFiles() + ) + ); + } catch (IOException e) { + logger.error("Unexpected error computing CopyState", e); + Assert.fail("Failed to compute copyState"); + } + } + + private void startReplicationAndAssertCancellation(IndexShard replica, SegmentReplicationTargetService targetService) + throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + final SegmentReplicationTarget target = targetService.startReplication( + ReplicationCheckpoint.empty(replica.shardId), + replica, + new SegmentReplicationTargetService.SegmentReplicationListener() { + @Override + public void onReplicationDone(SegmentReplicationState state) { + Assert.fail("Replication should not complete"); + } + + @Override + public void onReplicationFailure(SegmentReplicationState state, OpenSearchException e, boolean sendShardFailure) { + assertTrue(e instanceof CancellableThreads.ExecutionCancelledException); + assertFalse(sendShardFailure); + assertEquals(SegmentReplicationState.Stage.CANCELLED, state.getStage()); + latch.countDown(); + } + } + ); + + latch.await(2, TimeUnit.SECONDS); + assertEquals("Should have resolved listener with failure", 0, latch.getCount()); + assertNull(targetService.get(target.getId())); + } } diff --git a/server/src/test/java/org/opensearch/indices/cluster/IndicesClusterStateServiceRandomUpdatesTests.java b/server/src/test/java/org/opensearch/indices/cluster/IndicesClusterStateServiceRandomUpdatesTests.java index 1f2360abde2ad..22481b5a7b99f 100644 --- a/server/src/test/java/org/opensearch/indices/cluster/IndicesClusterStateServiceRandomUpdatesTests.java +++ b/server/src/test/java/org/opensearch/indices/cluster/IndicesClusterStateServiceRandomUpdatesTests.java @@ -66,6 +66,7 @@ import org.opensearch.index.shard.PrimaryReplicaSyncer; import org.opensearch.index.shard.ShardId; import org.opensearch.indices.recovery.PeerRecoveryTargetService; +import org.opensearch.indices.replication.SegmentReplicationSourceService; import org.opensearch.indices.replication.SegmentReplicationTargetService; import org.opensearch.indices.replication.checkpoint.SegmentReplicationCheckpointPublisher; import org.opensearch.repositories.RepositoriesService; @@ -572,6 +573,7 @@ private IndicesClusterStateService createIndicesClusterStateService( threadPool, SegmentReplicationCheckpointPublisher.EMPTY, SegmentReplicationTargetService.NO_OP, + SegmentReplicationSourceService.NO_OP, recoveryTargetService, shardStateAction, null, diff --git a/server/src/test/java/org/opensearch/indices/replication/OngoingSegmentReplicationsTests.java b/server/src/test/java/org/opensearch/indices/replication/OngoingSegmentReplicationsTests.java index 38c55620e1223..f49ee0471b5e8 100644 --- a/server/src/test/java/org/opensearch/indices/replication/OngoingSegmentReplicationsTests.java +++ b/server/src/test/java/org/opensearch/indices/replication/OngoingSegmentReplicationsTests.java @@ -14,6 +14,8 @@ import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.CancellableThreads; +import org.opensearch.common.xcontent.XContentType; import org.opensearch.index.IndexService; import org.opensearch.index.shard.IndexShard; import org.opensearch.index.shard.IndexShardTestCase; @@ -31,6 +33,8 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import static org.mockito.Mockito.any; import static org.mockito.Mockito.mock; @@ -154,6 +158,51 @@ public void testCancelReplication() throws IOException { assertEquals(0, replications.cachedCopyStateSize()); } + public void testCancelReplication_AfterSendFilesStarts() throws IOException, InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + OngoingSegmentReplications replications = new OngoingSegmentReplications(mockIndicesService, recoverySettings); + // add a doc and refresh so primary has more than one segment. + indexDoc(primary, "1", "{\"foo\" : \"baz\"}", XContentType.JSON, "foobar"); + primary.refresh("Test"); + final CheckpointInfoRequest request = new CheckpointInfoRequest( + 1L, + replica.routingEntry().allocationId().getId(), + primaryDiscoveryNode, + testCheckpoint + ); + final FileChunkWriter segmentSegmentFileChunkWriter = (fileMetadata, position, content, lastChunk, totalTranslogOps, listener) -> { + // cancel the replication as soon as the writer starts sending files. + replications.cancel(replica.routingEntry().allocationId().getId(), "Test"); + }; + final CopyState copyState = replications.prepareForReplication(request, segmentSegmentFileChunkWriter); + assertEquals(1, replications.size()); + assertEquals(1, replications.cachedCopyStateSize()); + getSegmentFilesRequest = new GetSegmentFilesRequest( + 1L, + replica.routingEntry().allocationId().getId(), + replicaDiscoveryNode, + new ArrayList<>(copyState.getMetadataSnapshot().asMap().values()), + testCheckpoint + ); + replications.startSegmentCopy(getSegmentFilesRequest, new ActionListener<>() { + @Override + public void onResponse(GetSegmentFilesResponse getSegmentFilesResponse) { + Assert.fail("Expected onFailure to be invoked."); + } + + @Override + public void onFailure(Exception e) { + assertEquals(CancellableThreads.ExecutionCancelledException.class, e.getClass()); + assertEquals(0, copyState.refCount()); + assertEquals(0, replications.size()); + assertEquals(0, replications.cachedCopyStateSize()); + latch.countDown(); + } + }); + latch.await(2, TimeUnit.SECONDS); + assertEquals("listener should have resolved with failure", 0, latch.getCount()); + } + public void testMultipleReplicasUseSameCheckpoint() throws IOException { IndexShard secondReplica = newShard(primary.shardId(), false); recoverReplica(secondReplica, primary, true); diff --git a/server/src/test/java/org/opensearch/indices/replication/PrimaryShardReplicationSourceTests.java b/server/src/test/java/org/opensearch/indices/replication/PrimaryShardReplicationSourceTests.java index 6bce74be569c3..323445bee1274 100644 --- a/server/src/test/java/org/opensearch/indices/replication/PrimaryShardReplicationSourceTests.java +++ b/server/src/test/java/org/opensearch/indices/replication/PrimaryShardReplicationSourceTests.java @@ -9,12 +9,14 @@ package org.opensearch.indices.replication; import org.apache.lucene.util.Version; +import org.junit.Assert; import org.opensearch.action.ActionListener; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.node.DiscoveryNodeRole; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.CancellableThreads; import org.opensearch.core.internal.io.IOUtils; import org.opensearch.index.shard.IndexShard; import org.opensearch.index.shard.IndexShardTestCase; @@ -28,6 +30,8 @@ import java.util.Arrays; import java.util.Collections; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import static org.mockito.Mockito.mock; @@ -126,6 +130,39 @@ public void testGetSegmentFiles() { assertTrue(capturedRequest.request instanceof GetSegmentFilesRequest); } + public void testGetSegmentFiles_CancelWhileRequestOpen() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + final ReplicationCheckpoint checkpoint = new ReplicationCheckpoint( + indexShard.shardId(), + PRIMARY_TERM, + SEGMENTS_GEN, + SEQ_NO, + VERSION + ); + StoreFileMetadata testMetadata = new StoreFileMetadata("testFile", 1L, "checksum", Version.LATEST); + replicationSource.getSegmentFiles( + REPLICATION_ID, + checkpoint, + Arrays.asList(testMetadata), + mock(Store.class), + new ActionListener<>() { + @Override + public void onResponse(GetSegmentFilesResponse getSegmentFilesResponse) { + Assert.fail("onFailure response expected."); + } + + @Override + public void onFailure(Exception e) { + assertEquals(e.getClass(), CancellableThreads.ExecutionCancelledException.class); + latch.countDown(); + } + } + ); + replicationSource.cancel(); + latch.await(2, TimeUnit.SECONDS); + assertEquals("listener should have resolved in a failure", 0, latch.getCount()); + } + private DiscoveryNode newDiscoveryNode(String nodeName) { return new DiscoveryNode( nodeName, diff --git a/server/src/test/java/org/opensearch/indices/replication/SegmentReplicationSourceHandlerTests.java b/server/src/test/java/org/opensearch/indices/replication/SegmentReplicationSourceHandlerTests.java index 2c52772649acc..a6e169dbc3d61 100644 --- a/server/src/test/java/org/opensearch/indices/replication/SegmentReplicationSourceHandlerTests.java +++ b/server/src/test/java/org/opensearch/indices/replication/SegmentReplicationSourceHandlerTests.java @@ -18,6 +18,7 @@ import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.CancellableThreads; import org.opensearch.index.shard.IndexShard; import org.opensearch.index.shard.IndexShardTestCase; import org.opensearch.index.store.StoreFileMetadata; @@ -28,6 +29,8 @@ import java.io.IOException; import java.util.Collections; import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import static org.mockito.Mockito.mock; @@ -197,4 +200,47 @@ public void testReplicationAlreadyRunning() throws IOException { handler.sendFiles(getSegmentFilesRequest, mock(ActionListener.class)); Assert.assertThrows(OpenSearchException.class, () -> { handler.sendFiles(getSegmentFilesRequest, mock(ActionListener.class)); }); } + + public void testCancelReplication() throws IOException, InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + chunkWriter = mock(FileChunkWriter.class); + + final ReplicationCheckpoint latestReplicationCheckpoint = primary.getLatestReplicationCheckpoint(); + final CopyState copyState = new CopyState(latestReplicationCheckpoint, primary); + SegmentReplicationSourceHandler handler = new SegmentReplicationSourceHandler( + localNode, + chunkWriter, + threadPool, + copyState, + primary.routingEntry().allocationId().getId(), + 5000, + 1 + ); + + final GetSegmentFilesRequest getSegmentFilesRequest = new GetSegmentFilesRequest( + 1L, + replica.routingEntry().allocationId().getId(), + replicaDiscoveryNode, + Collections.emptyList(), + latestReplicationCheckpoint + ); + + // cancel before xfer starts. Cancels during copy will be tested in SegmentFileTransferHandlerTests, that uses the same + // cancellableThreads. + handler.cancel("test"); + handler.sendFiles(getSegmentFilesRequest, new ActionListener<>() { + @Override + public void onResponse(GetSegmentFilesResponse getSegmentFilesResponse) { + Assert.fail("Expected failure."); + } + + @Override + public void onFailure(Exception e) { + assertEquals(CancellableThreads.ExecutionCancelledException.class, e.getClass()); + latch.countDown(); + } + }); + latch.await(2, TimeUnit.SECONDS); + assertEquals("listener should have resolved with failure", 0, latch.getCount()); + } } diff --git a/server/src/test/java/org/opensearch/indices/replication/SegmentReplicationTargetServiceTests.java b/server/src/test/java/org/opensearch/indices/replication/SegmentReplicationTargetServiceTests.java index de739f4ca834a..7d9b0f09f21cd 100644 --- a/server/src/test/java/org/opensearch/indices/replication/SegmentReplicationTargetServiceTests.java +++ b/server/src/test/java/org/opensearch/indices/replication/SegmentReplicationTargetServiceTests.java @@ -9,21 +9,22 @@ package org.opensearch.indices.replication; import org.junit.Assert; -import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import org.opensearch.OpenSearchException; import org.opensearch.action.ActionListener; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Settings; +import org.opensearch.index.engine.NRTReplicationEngineFactory; import org.opensearch.index.shard.IndexShard; import org.opensearch.index.shard.IndexShardTestCase; -import org.opensearch.indices.recovery.RecoverySettings; +import org.opensearch.index.store.Store; +import org.opensearch.index.store.StoreFileMetadata; import org.opensearch.indices.replication.checkpoint.ReplicationCheckpoint; -import org.opensearch.indices.replication.common.ReplicationLuceneIndex; -import org.opensearch.transport.TransportService; +import org.opensearch.indices.replication.common.ReplicationType; import java.io.IOException; +import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -35,12 +36,12 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.times; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.eq; public class SegmentReplicationTargetServiceTests extends IndexShardTestCase { - private IndexShard indexShard; + private IndexShard replicaShard; + private IndexShard primaryShard; private ReplicationCheckpoint checkpoint; private SegmentReplicationSource replicationSource; private SegmentReplicationTargetService sut; @@ -52,20 +53,20 @@ public class SegmentReplicationTargetServiceTests extends IndexShardTestCase { public void setUp() throws Exception { super.setUp(); final Settings settings = Settings.builder() - .put(IndexMetadata.SETTING_REPLICATION_TYPE, "SEGMENT") + .put(IndexMetadata.SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT) .put("node.name", SegmentReplicationTargetServiceTests.class.getSimpleName()) .build(); final ClusterSettings clusterSettings = new ClusterSettings(settings, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS); - final RecoverySettings recoverySettings = new RecoverySettings(settings, clusterSettings); - final TransportService transportService = mock(TransportService.class); - indexShard = newStartedShard(false, settings); - checkpoint = new ReplicationCheckpoint(indexShard.shardId(), 0L, 0L, 0L, 0L); + primaryShard = newStartedShard(true); + replicaShard = newShard(false, settings, new NRTReplicationEngineFactory()); + recoverReplica(replicaShard, primaryShard, true); + checkpoint = new ReplicationCheckpoint(replicaShard.shardId(), 0L, 0L, 0L, 0L); SegmentReplicationSourceFactory replicationSourceFactory = mock(SegmentReplicationSourceFactory.class); replicationSource = mock(SegmentReplicationSource.class); - when(replicationSourceFactory.get(indexShard)).thenReturn(replicationSource); + when(replicationSourceFactory.get(replicaShard)).thenReturn(replicationSource); - sut = new SegmentReplicationTargetService(threadPool, recoverySettings, transportService, replicationSourceFactory); - initialCheckpoint = indexShard.getLatestReplicationCheckpoint(); + sut = prepareForReplication(primaryShard); + initialCheckpoint = replicaShard.getLatestReplicationCheckpoint(); aheadCheckpoint = new ReplicationCheckpoint( initialCheckpoint.getShardId(), initialCheckpoint.getPrimaryTerm(), @@ -77,44 +78,58 @@ public void setUp() throws Exception { @Override public void tearDown() throws Exception { - closeShards(indexShard); + closeShards(primaryShard, replicaShard); super.tearDown(); } - public void testTargetReturnsSuccess_listenerCompletes() { - final SegmentReplicationTarget target = new SegmentReplicationTarget( - checkpoint, - indexShard, - replicationSource, - new SegmentReplicationTargetService.SegmentReplicationListener() { - @Override - public void onReplicationDone(SegmentReplicationState state) { - assertEquals(SegmentReplicationState.Stage.DONE, state.getStage()); - } + public void testsSuccessfulReplication_listenerCompletes() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + sut.startReplication(checkpoint, replicaShard, new SegmentReplicationTargetService.SegmentReplicationListener() { + @Override + public void onReplicationDone(SegmentReplicationState state) { + assertEquals(SegmentReplicationState.Stage.DONE, state.getStage()); + latch.countDown(); + } - @Override - public void onReplicationFailure(SegmentReplicationState state, OpenSearchException e, boolean sendShardFailure) { - Assert.fail(); - } + @Override + public void onReplicationFailure(SegmentReplicationState state, OpenSearchException e, boolean sendShardFailure) { + logger.error("Unexpected error", e); + Assert.fail("Test should succeed"); } - ); - final SegmentReplicationTarget spy = Mockito.spy(target); - doAnswer(invocation -> { - // set up stage correctly so the transition in markAsDone succeeds on listener completion - moveTargetToFinalStage(target); - final ActionListener listener = invocation.getArgument(0); - listener.onResponse(null); - return null; - }).when(spy).startReplication(any()); - sut.startReplication(spy); + }); + latch.await(2, TimeUnit.SECONDS); + assertEquals(0, latch.getCount()); } - public void testTargetThrowsException() { + public void testReplicationFails() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); final OpenSearchException expectedError = new OpenSearchException("Fail"); + SegmentReplicationSource source = new SegmentReplicationSource() { + + @Override + public void getCheckpointMetadata( + long replicationId, + ReplicationCheckpoint checkpoint, + ActionListener listener + ) { + listener.onFailure(expectedError); + } + + @Override + public void getSegmentFiles( + long replicationId, + ReplicationCheckpoint checkpoint, + List filesToFetch, + Store store, + ActionListener listener + ) { + Assert.fail("Should not be called"); + } + }; final SegmentReplicationTarget target = new SegmentReplicationTarget( checkpoint, - indexShard, - replicationSource, + replicaShard, + source, new SegmentReplicationTargetService.SegmentReplicationListener() { @Override public void onReplicationDone(SegmentReplicationState state) { @@ -123,24 +138,21 @@ public void onReplicationDone(SegmentReplicationState state) { @Override public void onReplicationFailure(SegmentReplicationState state, OpenSearchException e, boolean sendShardFailure) { - assertEquals(SegmentReplicationState.Stage.INIT, state.getStage()); + // failures leave state object in last entered stage. + assertEquals(SegmentReplicationState.Stage.GET_CHECKPOINT_INFO, state.getStage()); assertEquals(expectedError, e.getCause()); - assertTrue(sendShardFailure); + latch.countDown(); } } ); - final SegmentReplicationTarget spy = Mockito.spy(target); - doAnswer(invocation -> { - final ActionListener listener = invocation.getArgument(0); - listener.onFailure(expectedError); - return null; - }).when(spy).startReplication(any()); - sut.startReplication(spy); + sut.startReplication(target); + latch.await(2, TimeUnit.SECONDS); + assertEquals(0, latch.getCount()); } public void testAlreadyOnNewCheckpoint() { SegmentReplicationTargetService spy = spy(sut); - spy.onNewCheckpoint(indexShard.getLatestReplicationCheckpoint(), indexShard); + spy.onNewCheckpoint(replicaShard.getLatestReplicationCheckpoint(), replicaShard); verify(spy, times(0)).startReplication(any(), any(), any()); } @@ -149,7 +161,7 @@ public void testShardAlreadyReplicating() throws InterruptedException { SegmentReplicationTargetService serviceSpy = spy(sut); final SegmentReplicationTarget target = new SegmentReplicationTarget( checkpoint, - indexShard, + replicaShard, replicationSource, mock(SegmentReplicationTargetService.SegmentReplicationListener.class) ); @@ -161,7 +173,7 @@ public void testShardAlreadyReplicating() throws InterruptedException { doAnswer(invocation -> { final ActionListener listener = invocation.getArgument(0); // a new checkpoint arrives before we've completed. - serviceSpy.onNewCheckpoint(aheadCheckpoint, indexShard); + serviceSpy.onNewCheckpoint(aheadCheckpoint, replicaShard); listener.onResponse(null); latch.countDown(); return null; @@ -173,12 +185,12 @@ public void testShardAlreadyReplicating() throws InterruptedException { // wait for the new checkpoint to arrive, before the listener completes. latch.await(30, TimeUnit.SECONDS); - verify(serviceSpy, times(0)).startReplication(eq(aheadCheckpoint), eq(indexShard), any()); + verify(serviceSpy, times(0)).startReplication(eq(aheadCheckpoint), eq(replicaShard), any()); } public void testNewCheckpointBehindCurrentCheckpoint() { SegmentReplicationTargetService spy = spy(sut); - spy.onNewCheckpoint(checkpoint, indexShard); + spy.onNewCheckpoint(checkpoint, replicaShard); verify(spy, times(0)).startReplication(any(), any(), any()); } @@ -190,22 +202,6 @@ public void testShardNotStarted() throws IOException { closeShards(shard); } - public void testNewCheckpoint_validationPassesAndReplicationFails() throws IOException { - allowShardFailures(); - SegmentReplicationTargetService spy = spy(sut); - IndexShard spyShard = spy(indexShard); - ArgumentCaptor captor = ArgumentCaptor.forClass( - SegmentReplicationTargetService.SegmentReplicationListener.class - ); - doNothing().when(spy).startReplication(any(), any(), any()); - spy.onNewCheckpoint(aheadCheckpoint, spyShard); - verify(spy, times(1)).startReplication(any(), any(), captor.capture()); - SegmentReplicationTargetService.SegmentReplicationListener listener = captor.getValue(); - listener.onFailure(new SegmentReplicationState(new ReplicationLuceneIndex()), new OpenSearchException("testing"), true); - verify(spyShard).failShard(any(), any()); - closeShard(indexShard, false); - } - /** * here we are starting a new shard in PrimaryMode and testing that we don't process a checkpoint on shard when it is in PrimaryMode. */ @@ -215,70 +211,10 @@ public void testRejectCheckpointOnShardPrimaryMode() throws IOException { // Starting a new shard in PrimaryMode. IndexShard primaryShard = newStartedShard(true); IndexShard spyShard = spy(primaryShard); - doNothing().when(spy).startReplication(any(), any(), any()); spy.onNewCheckpoint(aheadCheckpoint, spyShard); // Verify that checkpoint is not processed as shard is in PrimaryMode. verify(spy, times(0)).startReplication(any(), any(), any()); closeShards(primaryShard); } - - public void testReplicationOnDone() throws IOException { - SegmentReplicationTargetService spy = spy(sut); - IndexShard spyShard = spy(indexShard); - ReplicationCheckpoint cp = indexShard.getLatestReplicationCheckpoint(); - ReplicationCheckpoint newCheckpoint = new ReplicationCheckpoint( - cp.getShardId(), - cp.getPrimaryTerm(), - cp.getSegmentsGen(), - cp.getSeqNo(), - cp.getSegmentInfosVersion() + 1 - ); - ReplicationCheckpoint anotherNewCheckpoint = new ReplicationCheckpoint( - cp.getShardId(), - cp.getPrimaryTerm(), - cp.getSegmentsGen(), - cp.getSeqNo(), - cp.getSegmentInfosVersion() + 2 - ); - ArgumentCaptor captor = ArgumentCaptor.forClass( - SegmentReplicationTargetService.SegmentReplicationListener.class - ); - doNothing().when(spy).startReplication(any(), any(), any()); - spy.onNewCheckpoint(newCheckpoint, spyShard); - spy.onNewCheckpoint(anotherNewCheckpoint, spyShard); - verify(spy, times(1)).startReplication(eq(newCheckpoint), any(), captor.capture()); - verify(spy, times(1)).onNewCheckpoint(eq(anotherNewCheckpoint), any()); - SegmentReplicationTargetService.SegmentReplicationListener listener = captor.getValue(); - listener.onDone(new SegmentReplicationState(new ReplicationLuceneIndex())); - doNothing().when(spy).onNewCheckpoint(any(), any()); - verify(spy, timeout(100).times(2)).onNewCheckpoint(eq(anotherNewCheckpoint), any()); - closeShard(indexShard, false); - } - - public void testBeforeIndexShardClosed_CancelsOngoingReplications() { - final SegmentReplicationTarget target = new SegmentReplicationTarget( - checkpoint, - indexShard, - replicationSource, - mock(SegmentReplicationTargetService.SegmentReplicationListener.class) - ); - final SegmentReplicationTarget spy = Mockito.spy(target); - sut.startReplication(spy); - sut.beforeIndexShardClosed(indexShard.shardId(), indexShard, Settings.EMPTY); - verify(spy, times(1)).cancel(any()); - } - - /** - * Move the {@link SegmentReplicationTarget} object through its {@link SegmentReplicationState.Stage} values in order - * until the final, non-terminal stage. - */ - private void moveTargetToFinalStage(SegmentReplicationTarget target) { - SegmentReplicationState.Stage[] stageValues = SegmentReplicationState.Stage.values(); - assertEquals(target.state().getStage(), SegmentReplicationState.Stage.INIT); - // Skip the first two stages (DONE and INIT) and iterate until the last value - for (int i = 2; i < stageValues.length; i++) { - target.state().setStage(stageValues[i]); - } - } } diff --git a/server/src/test/java/org/opensearch/snapshots/SnapshotResiliencyTests.java b/server/src/test/java/org/opensearch/snapshots/SnapshotResiliencyTests.java index 4d3b841e203de..ff4005d9bcedf 100644 --- a/server/src/test/java/org/opensearch/snapshots/SnapshotResiliencyTests.java +++ b/server/src/test/java/org/opensearch/snapshots/SnapshotResiliencyTests.java @@ -185,6 +185,7 @@ import org.opensearch.indices.recovery.PeerRecoveryTargetService; import org.opensearch.indices.recovery.RecoverySettings; import org.opensearch.indices.replication.SegmentReplicationSourceFactory; +import org.opensearch.indices.replication.SegmentReplicationSourceService; import org.opensearch.indices.replication.SegmentReplicationTargetService; import org.opensearch.indices.replication.checkpoint.SegmentReplicationCheckpointPublisher; import org.opensearch.ingest.IngestService; @@ -1857,6 +1858,7 @@ public void onFailure(final Exception e) { transportService, new SegmentReplicationSourceFactory(transportService, recoverySettings, clusterService) ), + SegmentReplicationSourceService.NO_OP, shardStateAction, new NodeMappingRefreshAction(transportService, metadataMappingService), repositoriesService, diff --git a/test/framework/src/main/java/org/opensearch/index/shard/IndexShardTestCase.java b/test/framework/src/main/java/org/opensearch/index/shard/IndexShardTestCase.java index 08004b7e42fea..1b40cb4f2dfa3 100644 --- a/test/framework/src/main/java/org/opensearch/index/shard/IndexShardTestCase.java +++ b/test/framework/src/main/java/org/opensearch/index/shard/IndexShardTestCase.java @@ -68,7 +68,6 @@ import org.opensearch.common.lucene.uid.Versions; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.TimeValue; import org.opensearch.common.util.BigArrays; import org.opensearch.common.xcontent.XContentType; import org.opensearch.core.internal.io.IOUtils; @@ -112,7 +111,10 @@ import org.opensearch.indices.replication.CheckpointInfoResponse; import org.opensearch.indices.replication.GetSegmentFilesResponse; import org.opensearch.indices.replication.SegmentReplicationSource; +import org.opensearch.indices.replication.SegmentReplicationSourceFactory; +import org.opensearch.indices.replication.SegmentReplicationState; import org.opensearch.indices.replication.SegmentReplicationTarget; +import org.opensearch.indices.replication.SegmentReplicationTargetService; import org.opensearch.indices.replication.checkpoint.ReplicationCheckpoint; import org.opensearch.indices.replication.checkpoint.SegmentReplicationCheckpointPublisher; import org.opensearch.indices.replication.common.CopyState; @@ -127,8 +129,10 @@ import org.opensearch.test.OpenSearchTestCase; import org.opensearch.threadpool.TestThreadPool; import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportService; import java.io.IOException; +import java.util.ArrayList; import java.nio.file.Path; import java.util.Arrays; import java.util.Collections; @@ -146,7 +150,9 @@ import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import static org.opensearch.cluster.routing.TestShardRouting.newShardRouting; /** @@ -1171,35 +1177,40 @@ public static Engine.Warmer createTestWarmer(IndexSettings indexSettings) { } /** - * Segment Replication specific test method - Replicate segments to a list of replicas from a given primary. - * This test will use a real {@link SegmentReplicationTarget} for each replica with a mock {@link SegmentReplicationSource} that - * writes all segments directly to the target. + * Segment Replication specific test method - Creates a {@link SegmentReplicationTargetService} to perform replications that has + * been configured to return the given primaryShard's current segments. + * + * @param primaryShard {@link IndexShard} - The primary shard to replicate from. */ - public final void replicateSegments(IndexShard primaryShard, List replicaShards) throws IOException, InterruptedException { - final CountDownLatch countDownLatch = new CountDownLatch(replicaShards.size()); - Store.MetadataSnapshot primaryMetadata; - try (final GatedCloseable segmentInfosSnapshot = primaryShard.getSegmentInfosSnapshot()) { - final SegmentInfos primarySegmentInfos = segmentInfosSnapshot.get(); - primaryMetadata = primaryShard.store().getMetadata(primarySegmentInfos); - } - final CopyState copyState = new CopyState(ReplicationCheckpoint.empty(primaryShard.shardId), primaryShard); - - final ReplicationCollection replicationCollection = new ReplicationCollection<>(logger, threadPool); - final SegmentReplicationSource source = new SegmentReplicationSource() { + public final SegmentReplicationTargetService prepareForReplication(IndexShard primaryShard) { + final SegmentReplicationSourceFactory sourceFactory = mock(SegmentReplicationSourceFactory.class); + final SegmentReplicationTargetService targetService = new SegmentReplicationTargetService( + threadPool, + new RecoverySettings(Settings.EMPTY, new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS)), + mock(TransportService.class), + sourceFactory + ); + final SegmentReplicationSource replicationSource = new SegmentReplicationSource() { @Override public void getCheckpointMetadata( long replicationId, ReplicationCheckpoint checkpoint, ActionListener listener ) { - listener.onResponse( - new CheckpointInfoResponse( - copyState.getCheckpoint(), - copyState.getMetadataSnapshot(), - copyState.getInfosBytes(), - copyState.getPendingDeleteFiles() - ) - ); + try { + final CopyState copyState = new CopyState(ReplicationCheckpoint.empty(primaryShard.shardId), primaryShard); + listener.onResponse( + new CheckpointInfoResponse( + copyState.getCheckpoint(), + copyState.getMetadataSnapshot(), + copyState.getInfosBytes(), + copyState.getPendingDeleteFiles() + ) + ); + } catch (IOException e) { + logger.error("Unexpected error computing CopyState", e); + Assert.fail("Failed to compute copyState"); + } } @Override @@ -1211,9 +1222,7 @@ public void getSegmentFiles( ActionListener listener ) { try ( - final ReplicationCollection.ReplicationRef replicationRef = replicationCollection.get( - replicationId - ) + final ReplicationCollection.ReplicationRef replicationRef = targetService.get(replicationId) ) { writeFileChunks(replicationRef.get(), primaryShard, filesToFetch.toArray(new StoreFileMetadata[] {})); } catch (IOException e) { @@ -1222,15 +1231,43 @@ public void getSegmentFiles( listener.onResponse(new GetSegmentFilesResponse(filesToFetch)); } }; + when(sourceFactory.get(any())).thenReturn(replicationSource); + return targetService; + } + /** + * Segment Replication specific test method - Replicate segments to a list of replicas from a given primary. + * This test will use a real {@link SegmentReplicationTarget} for each replica with a mock {@link SegmentReplicationSource} that + * writes all segments directly to the target. + * @param primaryShard - {@link IndexShard} The current primary shard. + * @param replicaShards - Replicas that will be updated. + * @return {@link List} List of target components orchestrating replication. + */ + public final List replicateSegments(IndexShard primaryShard, List replicaShards) + throws IOException, InterruptedException { + final SegmentReplicationTargetService targetService = prepareForReplication(primaryShard); + return replicateSegments(targetService, primaryShard, replicaShards); + } + + public final List replicateSegments( + SegmentReplicationTargetService targetService, + IndexShard primaryShard, + List replicaShards + ) throws IOException, InterruptedException { + final CountDownLatch countDownLatch = new CountDownLatch(replicaShards.size()); + Store.MetadataSnapshot primaryMetadata; + try (final GatedCloseable segmentInfosSnapshot = primaryShard.getSegmentInfosSnapshot()) { + final SegmentInfos primarySegmentInfos = segmentInfosSnapshot.get(); + primaryMetadata = primaryShard.store().getMetadata(primarySegmentInfos); + } + List ids = new ArrayList<>(); for (IndexShard replica : replicaShards) { - final SegmentReplicationTarget target = new SegmentReplicationTarget( + final SegmentReplicationTarget target = targetService.startReplication( ReplicationCheckpoint.empty(replica.shardId), replica, - source, - new ReplicationListener() { + new SegmentReplicationTargetService.SegmentReplicationListener() { @Override - public void onDone(ReplicationState state) { + public void onReplicationDone(SegmentReplicationState state) { try (final GatedCloseable snapshot = replica.getSegmentInfosSnapshot()) { final SegmentInfos replicaInfos = snapshot.get(); final Store.MetadataSnapshot replicaMetadata = replica.store().getMetadata(replicaInfos); @@ -1241,31 +1278,22 @@ public void onDone(ReplicationState state) { assertEquals(primaryMetadata.getCommitUserData(), replicaMetadata.getCommitUserData()); } catch (Exception e) { throw ExceptionsHelper.convertToRuntime(e); + } finally { + countDownLatch.countDown(); } - countDownLatch.countDown(); } @Override - public void onFailure(ReplicationState state, OpenSearchException e, boolean sendShardFailure) { + public void onReplicationFailure(SegmentReplicationState state, OpenSearchException e, boolean sendShardFailure) { logger.error("Unexpected replication failure in test", e); Assert.fail("test replication should not fail: " + e); } } ); - replicationCollection.start(target, TimeValue.timeValueMillis(5000)); - target.startReplication(new ActionListener<>() { - @Override - public void onResponse(Void o) { - replicationCollection.markAsDone(target.getId()); - } - - @Override - public void onFailure(Exception e) { - replicationCollection.fail(target.getId(), new OpenSearchException("Segment Replication failed", e), true); - } - }); + ids.add(target); + countDownLatch.await(1, TimeUnit.SECONDS); } - countDownLatch.await(3, TimeUnit.SECONDS); + return ids; } private void writeFileChunks(SegmentReplicationTarget target, IndexShard primary, StoreFileMetadata[] files) throws IOException { From 4a6e937b3ba120f38fa991c9c481a01daec18e94 Mon Sep 17 00:00:00 2001 From: Kunal Kotwani Date: Wed, 31 Aug 2022 18:49:19 -0700 Subject: [PATCH 071/187] Bug fixes for dependabot changelog verifier (#4364) * Fix token usage for changelog helper Signed-off-by: Kunal Kotwani * Add conditional check for dependabot steps Signed-off-by: Kunal Kotwani * Add dependency section Signed-off-by: Kunal Kotwani * Bug fixes for dependabot changelog verifier Signed-off-by: Kunal Kotwani * Update the changelog Signed-off-by: Kunal Kotwani Signed-off-by: Kunal Kotwani --- .github/pull_request_template.md | 2 +- .github/workflows/changelog_verifier.yml | 12 ------------ .github/workflows/dependabot_pr.yml | 14 ++++++++++++++ .linelint.yml | 1 + CHANGELOG.md | 1 + 5 files changed, 17 insertions(+), 13 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index c76e27d6dfc7d..4537cadf71074 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -10,7 +10,7 @@ - [ ] New functionality has been documented. - [ ] New functionality has javadoc added - [ ] Commits are signed per the DCO using --signoff -- [ ] Commit changes are listed out in CHANGELOG.md file (See: [Changelog](../CONTRIBUTING.md#changelog)) +- [ ] Commit changes are listed out in CHANGELOG.md file (See: [Changelog](../blob/main/CONTRIBUTING.md#changelog)) By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. For more information on following Developer Certificate of Origin and signing off your commits, please check [here](https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin). diff --git a/.github/workflows/changelog_verifier.yml b/.github/workflows/changelog_verifier.yml index ac0c0ec4d7297..cda5dde462068 100644 --- a/.github/workflows/changelog_verifier.yml +++ b/.github/workflows/changelog_verifier.yml @@ -13,16 +13,4 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} ref: ${{ github.event.pull_request.head.sha }} - - uses: dangoslen/dependabot-changelog-helper@v1 - with: - version: 'Unreleased' - - - uses: stefanzweifel/git-auto-commit-action@v4 - with: - commit_message: "Update changelog" - branch: ${{ github.head_ref }} - commit_user_name: dependabot[bot] - commit_user_email: support@github.com - commit_options: '--signoff' - - uses: dangoslen/changelog-enforcer@v3 diff --git a/.github/workflows/dependabot_pr.yml b/.github/workflows/dependabot_pr.yml index 2ac904bf4ccf7..ed98bae8978ed 100644 --- a/.github/workflows/dependabot_pr.yml +++ b/.github/workflows/dependabot_pr.yml @@ -47,3 +47,17 @@ jobs: commit_user_name: dependabot[bot] commit_user_email: support@github.com commit_options: '--signoff' + + - name: Update the changelog + uses: dangoslen/dependabot-changelog-helper@v1 + with: + version: 'Unreleased' + + - name: Commit the changes + uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: "Update changelog" + branch: ${{ github.head_ref }} + commit_user_name: dependabot[bot] + commit_user_email: support@github.com + commit_options: '--signoff' diff --git a/.linelint.yml b/.linelint.yml index 6240c8b3d7a96..ec947019f8ab6 100644 --- a/.linelint.yml +++ b/.linelint.yml @@ -7,6 +7,7 @@ ignore: - .idea/ - '*.sha1' - '*.txt' + - 'CHANGELOG.md' - '.github/CODEOWNERS' - 'buildSrc/src/testKit/opensearch.build/LICENSE' - 'buildSrc/src/testKit/opensearch.build/NOTICE' diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d07052d55ff0..07b08d853cf45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Add timeout on Mockito.verify to reduce flakyness in testReplicationOnDone test([#4314](https://github.com/opensearch-project/OpenSearch/pull/4314)) - Commit workflow for dependabot changelog helper ([#4331](https://github.com/opensearch-project/OpenSearch/pull/4331)) - Fixed cancellation of segment replication events ([#4225](https://github.com/opensearch-project/OpenSearch/pull/4225)) +- Bugs for dependabot changelog verifier workflow ([#4364](https://github.com/opensearch-project/OpenSearch/pull/4364)) ### Security - CVE-2022-25857 org.yaml:snakeyaml DOS vulnerability ([#4341](https://github.com/opensearch-project/OpenSearch/pull/4341)) From 689a2c44eee85b025c95516c4949d3a1bc3ec284 Mon Sep 17 00:00:00 2001 From: Bharathwaj G <58062316+bharath-techie@users.noreply.github.com> Date: Thu, 1 Sep 2022 10:58:45 +0530 Subject: [PATCH 072/187] Add changes for Create PIT and Delete PIT rest layer and rest high level client (#4064) * Create and delete PIT search rest layer changes Signed-off-by: Bharathwaj G --- CHANGELOG.md | 1 + .../opensearch/client/RequestConverters.java | 38 ++++- .../client/RestHighLevelClient.java | 118 ++++++++++++++++ .../java/org/opensearch/client/PitIT.java | 102 ++++++++++++++ .../client/RequestConvertersTests.java | 44 ++++++ .../client/RestHighLevelClientTests.java | 1 + .../java/org/opensearch/client/SearchIT.java | 47 +++++++ .../rest-api-spec/api/create_pit.json | 44 ++++++ .../rest-api-spec/api/delete_all_pits.json | 19 +++ .../rest-api-spec/api/delete_pit.json | 23 +++ .../rest-api-spec/test/pit/10_basic.yml | 130 +++++++++++++++++ .../org/opensearch/action/ActionModule.java | 10 ++ .../action/search/DeletePitInfo.java | 4 +- .../action/search/DeletePitRequest.java | 5 + .../action/search/GetAllPitNodesRequest.java | 11 ++ .../action/search/GetAllPitNodesResponse.java | 8 ++ .../action/search/NodesGetAllPitsAction.java | 23 +++ .../opensearch/action/search/PitService.java | 23 ++- .../search/TransportDeletePitAction.java | 6 +- .../search/TransportGetAllPitsAction.java | 80 +++-------- .../TransportNodesGetAllPitsAction.java | 86 +++++++++++ .../action/search/RestCreatePitAction.java | 57 ++++++++ .../action/search/RestDeletePitAction.java | 60 ++++++++ .../org/opensearch/search/SearchService.java | 4 +- .../search/CreatePitControllerTests.java | 11 +- .../search/TransportDeletePitActionTests.java | 18 +-- .../search/CreatePitSingleNodeTests.java | 52 +++++++ .../search/pit/RestCreatePitActionTests.java | 78 ++++++++++ .../search/pit/RestDeletePitActionTests.java | 133 ++++++++++++++++++ 29 files changed, 1152 insertions(+), 84 deletions(-) create mode 100644 client/rest-high-level/src/test/java/org/opensearch/client/PitIT.java create mode 100644 rest-api-spec/src/main/resources/rest-api-spec/api/create_pit.json create mode 100644 rest-api-spec/src/main/resources/rest-api-spec/api/delete_all_pits.json create mode 100644 rest-api-spec/src/main/resources/rest-api-spec/api/delete_pit.json create mode 100644 rest-api-spec/src/main/resources/rest-api-spec/test/pit/10_basic.yml create mode 100644 server/src/main/java/org/opensearch/action/search/NodesGetAllPitsAction.java create mode 100644 server/src/main/java/org/opensearch/action/search/TransportNodesGetAllPitsAction.java create mode 100644 server/src/main/java/org/opensearch/rest/action/search/RestCreatePitAction.java create mode 100644 server/src/main/java/org/opensearch/rest/action/search/RestDeletePitAction.java create mode 100644 server/src/test/java/org/opensearch/search/pit/RestCreatePitActionTests.java create mode 100644 server/src/test/java/org/opensearch/search/pit/RestDeletePitActionTests.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 07b08d853cf45..f89e7eba0698c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ## [Unreleased] ### Added - Github workflow for changelog verification ([#4085](https://github.com/opensearch-project/OpenSearch/pull/4085)) +- Point in time rest layer changes for create and delete PIT API ([#4064](https://github.com/opensearch-project/OpenSearch/pull/4064)) - Added @dreamer-89 as an Opensearch maintainer ([#4342](https://github.com/opensearch-project/OpenSearch/pull/4342)) - Added release notes for 1.3.5 ([#4343](https://github.com/opensearch-project/OpenSearch/pull/4343)) - Added release notes for 2.2.1 ([#4344](https://github.com/opensearch-project/OpenSearch/pull/4344)) diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/RequestConverters.java b/client/rest-high-level/src/main/java/org/opensearch/client/RequestConverters.java index 6fa57295f48e4..eedc27d1d2ea7 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/RequestConverters.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/RequestConverters.java @@ -54,6 +54,8 @@ import org.opensearch.action.get.MultiGetRequest; import org.opensearch.action.index.IndexRequest; import org.opensearch.action.search.ClearScrollRequest; +import org.opensearch.action.search.CreatePitRequest; +import org.opensearch.action.search.DeletePitRequest; import org.opensearch.action.search.MultiSearchRequest; import org.opensearch.action.search.SearchRequest; import org.opensearch.action.search.SearchScrollRequest; @@ -92,6 +94,7 @@ import org.opensearch.index.reindex.ReindexRequest; import org.opensearch.index.reindex.UpdateByQueryRequest; import org.opensearch.index.seqno.SequenceNumbers; +import org.opensearch.rest.action.search.RestCreatePitAction; import org.opensearch.rest.action.search.RestSearchAction; import org.opensearch.script.mustache.MultiSearchTemplateRequest; import org.opensearch.script.mustache.SearchTemplateRequest; @@ -433,9 +436,19 @@ static void addSearchRequestParams(Params params, SearchRequest searchRequest) { params.putParam(RestSearchAction.TYPED_KEYS_PARAM, "true"); params.withRouting(searchRequest.routing()); params.withPreference(searchRequest.preference()); - params.withIndicesOptions(searchRequest.indicesOptions()); + if (searchRequest.pointInTimeBuilder() == null) { + params.withIndicesOptions(searchRequest.indicesOptions()); + } params.withSearchType(searchRequest.searchType().name().toLowerCase(Locale.ROOT)); - params.putParam("ccs_minimize_roundtrips", Boolean.toString(searchRequest.isCcsMinimizeRoundtrips())); + /** + * Merging search responses as part of CCS flow to reduce roundtrips is not supported for point in time - + * refer to org.opensearch.action.search.SearchResponseMerger + */ + if (searchRequest.pointInTimeBuilder() != null) { + params.putParam("ccs_minimize_roundtrips", "false"); + } else { + params.putParam("ccs_minimize_roundtrips", Boolean.toString(searchRequest.isCcsMinimizeRoundtrips())); + } if (searchRequest.getPreFilterShardSize() != null) { params.putParam("pre_filter_shard_size", Integer.toString(searchRequest.getPreFilterShardSize())); } @@ -464,6 +477,27 @@ static Request clearScroll(ClearScrollRequest clearScrollRequest) throws IOExcep return request; } + static Request createPit(CreatePitRequest createPitRequest) throws IOException { + Params params = new Params(); + params.putParam(RestCreatePitAction.ALLOW_PARTIAL_PIT_CREATION, Boolean.toString(createPitRequest.shouldAllowPartialPitCreation())); + params.putParam(RestCreatePitAction.KEEP_ALIVE, createPitRequest.getKeepAlive()); + params.withIndicesOptions(createPitRequest.indicesOptions()); + Request request = new Request(HttpPost.METHOD_NAME, endpoint(createPitRequest.indices(), "_search/point_in_time")); + request.addParameters(params.asMap()); + request.setEntity(createEntity(createPitRequest, REQUEST_BODY_CONTENT_TYPE)); + return request; + } + + static Request deletePit(DeletePitRequest deletePitRequest) throws IOException { + Request request = new Request(HttpDelete.METHOD_NAME, "/_search/point_in_time"); + request.setEntity(createEntity(deletePitRequest, REQUEST_BODY_CONTENT_TYPE)); + return request; + } + + static Request deleteAllPits() { + return new Request(HttpDelete.METHOD_NAME, "/_search/point_in_time/_all"); + } + static Request multiSearch(MultiSearchRequest multiSearchRequest) throws IOException { Request request = new Request(HttpPost.METHOD_NAME, "/_msearch"); diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/RestHighLevelClient.java b/client/rest-high-level/src/main/java/org/opensearch/client/RestHighLevelClient.java index 28a441bdf7f7f..0c73c65f6175f 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/RestHighLevelClient.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/RestHighLevelClient.java @@ -59,6 +59,10 @@ import org.opensearch.action.index.IndexResponse; import org.opensearch.action.search.ClearScrollRequest; import org.opensearch.action.search.ClearScrollResponse; +import org.opensearch.action.search.CreatePitRequest; +import org.opensearch.action.search.CreatePitResponse; +import org.opensearch.action.search.DeletePitRequest; +import org.opensearch.action.search.DeletePitResponse; import org.opensearch.action.search.MultiSearchRequest; import org.opensearch.action.search.MultiSearchResponse; import org.opensearch.action.search.SearchRequest; @@ -1250,6 +1254,120 @@ public final Cancellable scrollAsync( ); } + /** + * Create PIT context using create PIT API + * + * @param createPitRequest the request + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @return the response + */ + public final CreatePitResponse createPit(CreatePitRequest createPitRequest, RequestOptions options) throws IOException { + return performRequestAndParseEntity( + createPitRequest, + RequestConverters::createPit, + options, + CreatePitResponse::fromXContent, + emptySet() + ); + } + + /** + * Asynchronously Create PIT context using create PIT API + * + * @param createPitRequest the request + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @param listener the listener to be notified upon request completion + * @return the response + */ + public final Cancellable createPitAsync( + CreatePitRequest createPitRequest, + RequestOptions options, + ActionListener listener + ) { + return performRequestAsyncAndParseEntity( + createPitRequest, + RequestConverters::createPit, + options, + CreatePitResponse::fromXContent, + listener, + emptySet() + ); + } + + /** + * Delete point in time searches using delete PIT API + * + * @param deletePitRequest the request + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @return the response + */ + public final DeletePitResponse deletePit(DeletePitRequest deletePitRequest, RequestOptions options) throws IOException { + return performRequestAndParseEntity( + deletePitRequest, + RequestConverters::deletePit, + options, + DeletePitResponse::fromXContent, + emptySet() + ); + } + + /** + * Asynchronously Delete point in time searches using delete PIT API + * + * @param deletePitRequest the request + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @param listener the listener to be notified upon request completion + * @return the response + */ + public final Cancellable deletePitAsync( + DeletePitRequest deletePitRequest, + RequestOptions options, + ActionListener listener + ) { + return performRequestAsyncAndParseEntity( + deletePitRequest, + RequestConverters::deletePit, + options, + DeletePitResponse::fromXContent, + listener, + emptySet() + ); + } + + /** + * Delete all point in time searches using delete all PITs API + * + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @return the response + */ + public final DeletePitResponse deleteAllPits(RequestOptions options) throws IOException { + return performRequestAndParseEntity( + new MainRequest(), + (request) -> RequestConverters.deleteAllPits(), + options, + DeletePitResponse::fromXContent, + emptySet() + ); + } + + /** + * Asynchronously Delete all point in time searches using delete all PITs API + * + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @param listener the listener to be notified upon request completion + * @return the response + */ + public final Cancellable deleteAllPitsAsync(RequestOptions options, ActionListener listener) { + return performRequestAsyncAndParseEntity( + new MainRequest(), + (request) -> RequestConverters.deleteAllPits(), + options, + DeletePitResponse::fromXContent, + listener, + emptySet() + ); + } + /** * Clears one or more scroll ids using the Clear Scroll API. * diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/PitIT.java b/client/rest-high-level/src/test/java/org/opensearch/client/PitIT.java new file mode 100644 index 0000000000000..395ec6e46a7b3 --- /dev/null +++ b/client/rest-high-level/src/test/java/org/opensearch/client/PitIT.java @@ -0,0 +1,102 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.client; + +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.junit.Before; +import org.opensearch.OpenSearchStatusException; +import org.opensearch.action.ActionListener; +import org.opensearch.action.search.CreatePitRequest; +import org.opensearch.action.search.CreatePitResponse; +import org.opensearch.action.search.DeletePitInfo; +import org.opensearch.action.search.DeletePitRequest; +import org.opensearch.action.search.DeletePitResponse; +import org.opensearch.common.unit.TimeValue; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + * Tests point in time API with rest high level client + */ +public class PitIT extends OpenSearchRestHighLevelClientTestCase { + + @Before + public void indexDocuments() throws IOException { + Request doc1 = new Request(HttpPut.METHOD_NAME, "/index/_doc/1"); + doc1.setJsonEntity("{\"type\":\"type1\", \"id\":1, \"num\":10, \"num2\":50}"); + client().performRequest(doc1); + Request doc2 = new Request(HttpPut.METHOD_NAME, "/index/_doc/2"); + doc2.setJsonEntity("{\"type\":\"type1\", \"id\":2, \"num\":20, \"num2\":40}"); + client().performRequest(doc2); + Request doc3 = new Request(HttpPut.METHOD_NAME, "/index/_doc/3"); + doc3.setJsonEntity("{\"type\":\"type1\", \"id\":3, \"num\":50, \"num2\":35}"); + client().performRequest(doc3); + Request doc4 = new Request(HttpPut.METHOD_NAME, "/index/_doc/4"); + doc4.setJsonEntity("{\"type\":\"type2\", \"id\":4, \"num\":100, \"num2\":10}"); + client().performRequest(doc4); + Request doc5 = new Request(HttpPut.METHOD_NAME, "/index/_doc/5"); + doc5.setJsonEntity("{\"type\":\"type2\", \"id\":5, \"num\":100, \"num2\":10}"); + client().performRequest(doc5); + client().performRequest(new Request(HttpPost.METHOD_NAME, "/_refresh")); + } + + public void testCreateAndDeletePit() throws IOException { + CreatePitRequest pitRequest = new CreatePitRequest(new TimeValue(1, TimeUnit.DAYS), true, "index"); + CreatePitResponse pitResponse = execute(pitRequest, highLevelClient()::createPit, highLevelClient()::createPitAsync); + assertTrue(pitResponse.getId() != null); + assertEquals(1, pitResponse.getTotalShards()); + assertEquals(1, pitResponse.getSuccessfulShards()); + assertEquals(0, pitResponse.getFailedShards()); + assertEquals(0, pitResponse.getSkippedShards()); + List pitIds = new ArrayList<>(); + pitIds.add(pitResponse.getId()); + DeletePitRequest deletePitRequest = new DeletePitRequest(pitIds); + DeletePitResponse deletePitResponse = execute(deletePitRequest, highLevelClient()::deletePit, highLevelClient()::deletePitAsync); + assertTrue(deletePitResponse.getDeletePitResults().get(0).isSuccessful()); + assertTrue(deletePitResponse.getDeletePitResults().get(0).getPitId().equals(pitResponse.getId())); + } + + public void testDeleteAllPits() throws IOException { + CreatePitRequest pitRequest = new CreatePitRequest(new TimeValue(1, TimeUnit.DAYS), true, "index"); + CreatePitResponse pitResponse = execute(pitRequest, highLevelClient()::createPit, highLevelClient()::createPitAsync); + CreatePitResponse pitResponse1 = execute(pitRequest, highLevelClient()::createPit, highLevelClient()::createPitAsync); + assertTrue(pitResponse.getId() != null); + assertTrue(pitResponse1.getId() != null); + DeletePitResponse deletePitResponse = highLevelClient().deleteAllPits(RequestOptions.DEFAULT); + for (DeletePitInfo deletePitInfo : deletePitResponse.getDeletePitResults()) { + assertTrue(deletePitInfo.isSuccessful()); + } + pitResponse = execute(pitRequest, highLevelClient()::createPit, highLevelClient()::createPitAsync); + pitResponse1 = execute(pitRequest, highLevelClient()::createPit, highLevelClient()::createPitAsync); + assertTrue(pitResponse.getId() != null); + assertTrue(pitResponse1.getId() != null); + ActionListener deletePitListener = new ActionListener<>() { + @Override + public void onResponse(DeletePitResponse response) { + for (DeletePitInfo deletePitInfo : response.getDeletePitResults()) { + assertTrue(deletePitInfo.isSuccessful()); + } + } + + @Override + public void onFailure(Exception e) { + if (!(e instanceof OpenSearchStatusException)) { + throw new AssertionError("Delete all failed"); + } + } + }; + highLevelClient().deleteAllPitsAsync(RequestOptions.DEFAULT, deletePitListener); + // validate no pits case + highLevelClient().deleteAllPitsAsync(RequestOptions.DEFAULT, deletePitListener); + } +} diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/RequestConvertersTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/RequestConvertersTests.java index 97c0f2f475826..ee5795deb165d 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/RequestConvertersTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/RequestConvertersTests.java @@ -53,6 +53,8 @@ import org.opensearch.action.get.MultiGetRequest; import org.opensearch.action.index.IndexRequest; import org.opensearch.action.search.ClearScrollRequest; +import org.opensearch.action.search.CreatePitRequest; +import org.opensearch.action.search.DeletePitRequest; import org.opensearch.action.search.MultiSearchRequest; import org.opensearch.action.search.SearchRequest; import org.opensearch.action.search.SearchScrollRequest; @@ -131,6 +133,7 @@ import java.util.Locale; import java.util.Map; import java.util.StringJoiner; +import java.util.concurrent.TimeUnit; import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; @@ -1303,6 +1306,47 @@ public void testClearScroll() throws IOException { assertEquals(REQUEST_BODY_CONTENT_TYPE.mediaTypeWithoutParameters(), request.getEntity().getContentType().getValue()); } + public void testCreatePit() throws IOException { + String[] indices = randomIndicesNames(0, 5); + Map expectedParams = new HashMap<>(); + expectedParams.put("keep_alive", "1d"); + expectedParams.put("allow_partial_pit_creation", "true"); + CreatePitRequest createPitRequest = new CreatePitRequest(new TimeValue(1, TimeUnit.DAYS), true, indices); + setRandomIndicesOptions(createPitRequest::indicesOptions, createPitRequest::indicesOptions, expectedParams); + Request request = RequestConverters.createPit(createPitRequest); + StringJoiner endpoint = new StringJoiner("/", "/", ""); + String index = String.join(",", indices); + if (Strings.hasLength(index)) { + endpoint.add(index); + } + endpoint.add("_search/point_in_time"); + assertEquals(HttpPost.METHOD_NAME, request.getMethod()); + assertEquals(endpoint.toString(), request.getEndpoint()); + assertEquals(expectedParams, request.getParameters()); + assertToXContentBody(createPitRequest, request.getEntity()); + assertEquals(REQUEST_BODY_CONTENT_TYPE.mediaTypeWithoutParameters(), request.getEntity().getContentType().getValue()); + } + + public void testDeletePit() throws IOException { + List pitIdsList = new ArrayList<>(); + pitIdsList.add("pitId1"); + pitIdsList.add("pitId2"); + DeletePitRequest deletePitRequest = new DeletePitRequest(pitIdsList); + Request request = RequestConverters.deletePit(deletePitRequest); + String endpoint = "/_search/point_in_time"; + assertEquals(HttpDelete.METHOD_NAME, request.getMethod()); + assertEquals(endpoint, request.getEndpoint()); + assertToXContentBody(deletePitRequest, request.getEntity()); + assertEquals(REQUEST_BODY_CONTENT_TYPE.mediaTypeWithoutParameters(), request.getEntity().getContentType().getValue()); + } + + public void testDeleteAllPits() { + Request request = RequestConverters.deleteAllPits(); + String endpoint = "/_search/point_in_time/_all"; + assertEquals(HttpDelete.METHOD_NAME, request.getMethod()); + assertEquals(endpoint, request.getEndpoint()); + } + public void testSearchTemplate() throws Exception { // Create a random request. String[] indices = randomIndicesNames(0, 5); diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/RestHighLevelClientTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/RestHighLevelClientTests.java index 3da0f81023f72..cdd63743f2644 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/RestHighLevelClientTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/RestHighLevelClientTests.java @@ -134,6 +134,7 @@ public class RestHighLevelClientTests extends OpenSearchTestCase { // core "ping", "info", + "delete_all_pits", // security "security.get_ssl_certificates", "security.authenticate", diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/SearchIT.java b/client/rest-high-level/src/test/java/org/opensearch/client/SearchIT.java index 19e287fb91be5..8b509e5d19e92 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/SearchIT.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/SearchIT.java @@ -43,6 +43,10 @@ import org.opensearch.action.fieldcaps.FieldCapabilitiesResponse; import org.opensearch.action.search.ClearScrollRequest; import org.opensearch.action.search.ClearScrollResponse; +import org.opensearch.action.search.CreatePitRequest; +import org.opensearch.action.search.CreatePitResponse; +import org.opensearch.action.search.DeletePitRequest; +import org.opensearch.action.search.DeletePitResponse; import org.opensearch.action.search.MultiSearchRequest; import org.opensearch.action.search.MultiSearchResponse; import org.opensearch.action.search.SearchRequest; @@ -89,6 +93,7 @@ import org.opensearch.search.aggregations.metrics.WeightedAvgAggregationBuilder; import org.opensearch.search.aggregations.support.MultiValuesSourceFieldConfig; import org.opensearch.search.aggregations.support.ValueType; +import org.opensearch.search.builder.PointInTimeBuilder; import org.opensearch.search.builder.SearchSourceBuilder; import org.opensearch.search.fetch.subphase.FetchSourceContext; import org.opensearch.search.fetch.subphase.highlight.HighlightBuilder; @@ -100,11 +105,13 @@ import org.junit.Before; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.TimeUnit; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertToXContentEquivalent; @@ -762,6 +769,46 @@ public void testSearchScroll() throws Exception { } } + public void testSearchWithPit() throws Exception { + for (int i = 0; i < 100; i++) { + XContentBuilder builder = jsonBuilder().startObject().field("field", i).endObject(); + Request doc = new Request(HttpPut.METHOD_NAME, "/test/_doc/" + Integer.toString(i)); + doc.setJsonEntity(Strings.toString(builder)); + client().performRequest(doc); + } + client().performRequest(new Request(HttpPost.METHOD_NAME, "/test/_refresh")); + + CreatePitRequest pitRequest = new CreatePitRequest(new TimeValue(1, TimeUnit.DAYS), true, "test"); + CreatePitResponse pitResponse = execute(pitRequest, highLevelClient()::createPit, highLevelClient()::createPitAsync); + + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder().size(35) + .sort("field", SortOrder.ASC) + .pointInTimeBuilder(new PointInTimeBuilder(pitResponse.getId())); + SearchRequest searchRequest = new SearchRequest().source(searchSourceBuilder); + SearchResponse searchResponse = execute(searchRequest, highLevelClient()::search, highLevelClient()::searchAsync); + + try { + long counter = 0; + assertSearchHeader(searchResponse); + assertThat(searchResponse.getHits().getTotalHits().value, equalTo(100L)); + assertThat(searchResponse.getHits().getHits().length, equalTo(35)); + for (SearchHit hit : searchResponse.getHits()) { + assertThat(((Number) hit.getSortValues()[0]).longValue(), equalTo(counter++)); + } + } finally { + List pitIds = new ArrayList<>(); + pitIds.add(pitResponse.getId()); + DeletePitRequest deletePitRequest = new DeletePitRequest(pitIds); + DeletePitResponse deletePitResponse = execute( + deletePitRequest, + highLevelClient()::deletePit, + highLevelClient()::deletePitAsync + ); + assertTrue(deletePitResponse.getDeletePitResults().get(0).isSuccessful()); + assertTrue(deletePitResponse.getDeletePitResults().get(0).getPitId().equals(pitResponse.getId())); + } + } + public void testMultiSearch() throws Exception { MultiSearchRequest multiSearchRequest = new MultiSearchRequest(); SearchRequest searchRequest1 = new SearchRequest("index1"); diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/create_pit.json b/rest-api-spec/src/main/resources/rest-api-spec/api/create_pit.json new file mode 100644 index 0000000000000..d3a2104c01bc0 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/create_pit.json @@ -0,0 +1,44 @@ + +{ + "create_pit":{ + "documentation":{ + "url":"https://opensearch.org/docs/latest/opensearch/rest-api/point_in_time/", + "description":"Creates point in time context." + }, + "stability":"stable", + "url":{ + "paths":[ + { + "path":"/{index}/_search/point_in_time", + "methods":[ + "POST" + ], + "parts":{ + "index":{ + "type":"list", + "description":"A comma-separated list of index names to search; use `_all` or empty string to perform the operation on all indices" + } + } + } + ] + }, + "params":{ + "allow_partial_pit_creation":{ + "type":"boolean", + "description":"Allow if point in time can be created with partial failures" + }, + "keep_alive":{ + "type":"string", + "description":"Specify the keep alive for point in time" + }, + "preference":{ + "type":"string", + "description":"Specify the node or shard the operation should be performed on (default: random)" + }, + "routing":{ + "type":"list", + "description":"A comma-separated list of specific routing values" + } + } + } +} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/delete_all_pits.json b/rest-api-spec/src/main/resources/rest-api-spec/api/delete_all_pits.json new file mode 100644 index 0000000000000..5ff01aa746df9 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/delete_all_pits.json @@ -0,0 +1,19 @@ +{ + "delete_all_pits":{ + "documentation":{ + "url":"https://opensearch.org/docs/latest/opensearch/rest-api/point_in_time/", + "description":"Deletes all active point in time searches." + }, + "stability":"stable", + "url":{ + "paths":[ + { + "path":"/_search/point_in_time/_all", + "methods":[ + "DELETE" + ] + } + ] + } + } +} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/delete_pit.json b/rest-api-spec/src/main/resources/rest-api-spec/api/delete_pit.json new file mode 100644 index 0000000000000..b54d9f76204f4 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/delete_pit.json @@ -0,0 +1,23 @@ +{ + "delete_pit":{ + "documentation":{ + "url":"https://opensearch.org/docs/latest/opensearch/rest-api/point_in_time/", + "description":"Deletes one or more point in time searches based on the IDs passed." + }, + "stability":"stable", + "url":{ + "paths":[ + { + "path":"/_search/point_in_time", + "methods":[ + "DELETE" + ] + } + ] + }, + "body":{ + "description":"A comma-separated list of pit IDs to clear", + "required":true + } + } +} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/pit/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/pit/10_basic.yml new file mode 100644 index 0000000000000..2023bcc8f5c87 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/pit/10_basic.yml @@ -0,0 +1,130 @@ +"Create PIT, Search with PIT ID and Delete": + - skip: + version: " - 2.9.99" + reason: "mode to be introduced later than 3.0" + - do: + indices.create: + index: test_pit + - do: + index: + index: test_pit + id: 42 + body: { foo: 1 } + + - do: + index: + index: test_pit + id: 43 + body: { foo: 2 } + + - do: + indices.refresh: {} + + - do: + create_pit: + allow_partial_pit_creation: true + index: test_pit + keep_alive: 23h + + - set: {pit_id: pit_id} + - match: { _shards.failed: 0} + - do: + search: + rest_total_hits_as_int: true + size: 1 + sort: foo + body: + query: + match_all: {} + pit: {"id": "$pit_id"} + + - match: {hits.total: 2 } + - length: {hits.hits: 1 } + - match: {hits.hits.0._id: "42" } + + - do: + index: + index: test_pit + id: 44 + body: { foo: 3 } + + - do: + indices.refresh: {} + + - do: + search: + rest_total_hits_as_int: true + size: 1 + sort: foo + body: + query: + match_all: {} + pit: {"id": "$pit_id", "keep_alive":"10m"} + + - match: {hits.total: 2 } + - length: {hits.hits: 1 } + - match: {hits.hits.0._id: "42" } + + + - do: + search: + rest_total_hits_as_int: true + index: test_pit + size: 1 + sort: foo + body: + query: + match_all: {} + + - match: {hits.total: 3 } + - length: {hits.hits: 1 } + + - do: + delete_pit: + body: + "pit_id": [$pit_id] + + - match: {pits.0.pit_id: $pit_id} + - match: {pits.0.successful: true } + +--- +"Delete all": + - skip: + version: " - 2.9.99" + reason: "mode to be introduced later than 3.0" + - do: + indices.create: + index: test_pit + - do: + index: + index: test_pit + id: 42 + body: { foo: 1 } + + - do: + index: + index: test_pit + id: 43 + body: { foo: 2 } + + - do: + indices.refresh: {} + + - do: + create_pit: + allow_partial_pit_creation: true + index: test_pit + keep_alive: 23h + + - set: {pit_id: pit_id} + - match: { _shards.failed: 0} + + - do: + delete_all_pits: {} + + - match: {pits.0.pit_id: $pit_id} + - match: {pits.0.successful: true } + + - do: + catch: missing + delete_all_pits: { } diff --git a/server/src/main/java/org/opensearch/action/ActionModule.java b/server/src/main/java/org/opensearch/action/ActionModule.java index 797c5c38fada6..74be544123d9f 100644 --- a/server/src/main/java/org/opensearch/action/ActionModule.java +++ b/server/src/main/java/org/opensearch/action/ActionModule.java @@ -240,12 +240,14 @@ import org.opensearch.action.search.DeletePitAction; import org.opensearch.action.search.GetAllPitsAction; import org.opensearch.action.search.MultiSearchAction; +import org.opensearch.action.search.NodesGetAllPitsAction; import org.opensearch.action.search.SearchAction; import org.opensearch.action.search.SearchScrollAction; import org.opensearch.action.search.TransportClearScrollAction; import org.opensearch.action.search.TransportCreatePitAction; import org.opensearch.action.search.TransportDeletePitAction; import org.opensearch.action.search.TransportGetAllPitsAction; +import org.opensearch.action.search.TransportNodesGetAllPitsAction; import org.opensearch.action.search.TransportMultiSearchAction; import org.opensearch.action.search.TransportSearchAction; import org.opensearch.action.search.TransportSearchScrollAction; @@ -408,6 +410,8 @@ import org.opensearch.rest.action.ingest.RestSimulatePipelineAction; import org.opensearch.rest.action.search.RestClearScrollAction; import org.opensearch.rest.action.search.RestCountAction; +import org.opensearch.rest.action.search.RestCreatePitAction; +import org.opensearch.rest.action.search.RestDeletePitAction; import org.opensearch.rest.action.search.RestExplainAction; import org.opensearch.rest.action.search.RestMultiSearchAction; import org.opensearch.rest.action.search.RestSearchAction; @@ -674,6 +678,7 @@ public void reg actions.register(GetAllPitsAction.INSTANCE, TransportGetAllPitsAction.class); actions.register(DeletePitAction.INSTANCE, TransportDeletePitAction.class); actions.register(PitSegmentsAction.INSTANCE, TransportPitSegmentsAction.class); + actions.register(NodesGetAllPitsAction.INSTANCE, TransportNodesGetAllPitsAction.class); // Remote Store actions.register(RestoreRemoteStoreAction.INSTANCE, TransportRestoreRemoteStoreAction.class); @@ -849,6 +854,11 @@ public void initRestHandlers(Supplier nodesInCluster) { registerHandler.accept(new RestRepositoriesAction()); registerHandler.accept(new RestSnapshotAction()); registerHandler.accept(new RestTemplatesAction()); + + // Point in time API + registerHandler.accept(new RestCreatePitAction()); + registerHandler.accept(new RestDeletePitAction()); + for (ActionPlugin plugin : actionPlugins) { for (RestHandler handler : plugin.getRestHandlers( settings, diff --git a/server/src/main/java/org/opensearch/action/search/DeletePitInfo.java b/server/src/main/java/org/opensearch/action/search/DeletePitInfo.java index 943199812771a..5a167c5a6f160 100644 --- a/server/src/main/java/org/opensearch/action/search/DeletePitInfo.java +++ b/server/src/main/java/org/opensearch/action/search/DeletePitInfo.java @@ -65,11 +65,11 @@ public void writeTo(StreamOutput out) throws IOException { static { PARSER.declareBoolean(constructorArg(), new ParseField("successful")); - PARSER.declareString(constructorArg(), new ParseField("pitId")); + PARSER.declareString(constructorArg(), new ParseField("pit_id")); } private static final ParseField SUCCESSFUL = new ParseField("successful"); - private static final ParseField PIT_ID = new ParseField("pitId"); + private static final ParseField PIT_ID = new ParseField("pit_id"); @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { diff --git a/server/src/main/java/org/opensearch/action/search/DeletePitRequest.java b/server/src/main/java/org/opensearch/action/search/DeletePitRequest.java index 945fcfd17eb6c..926e9c19a33f5 100644 --- a/server/src/main/java/org/opensearch/action/search/DeletePitRequest.java +++ b/server/src/main/java/org/opensearch/action/search/DeletePitRequest.java @@ -48,6 +48,11 @@ public DeletePitRequest(List pitIds) { this.pitIds.addAll(pitIds); } + public void clearAndSetPitIds(List pitIds) { + this.pitIds.clear(); + this.pitIds.addAll(pitIds); + } + public DeletePitRequest() {} public List getPitIds() { diff --git a/server/src/main/java/org/opensearch/action/search/GetAllPitNodesRequest.java b/server/src/main/java/org/opensearch/action/search/GetAllPitNodesRequest.java index b4ad2f6641087..340f9b842adbf 100644 --- a/server/src/main/java/org/opensearch/action/search/GetAllPitNodesRequest.java +++ b/server/src/main/java/org/opensearch/action/search/GetAllPitNodesRequest.java @@ -21,11 +21,22 @@ */ public class GetAllPitNodesRequest extends BaseNodesRequest { + // Security plugin intercepts and sets the response with permitted PIT contexts + private GetAllPitNodesResponse getAllPitNodesResponse; + @Inject public GetAllPitNodesRequest(DiscoveryNode... concreteNodes) { super(concreteNodes); } + public void setGetAllPitNodesResponse(GetAllPitNodesResponse getAllPitNodesResponse) { + this.getAllPitNodesResponse = getAllPitNodesResponse; + } + + public GetAllPitNodesResponse getGetAllPitNodesResponse() { + return getAllPitNodesResponse; + } + public GetAllPitNodesRequest(StreamInput in) throws IOException { super(in); } diff --git a/server/src/main/java/org/opensearch/action/search/GetAllPitNodesResponse.java b/server/src/main/java/org/opensearch/action/search/GetAllPitNodesResponse.java index 4a454e7145eff..091447798cf5f 100644 --- a/server/src/main/java/org/opensearch/action/search/GetAllPitNodesResponse.java +++ b/server/src/main/java/org/opensearch/action/search/GetAllPitNodesResponse.java @@ -52,6 +52,14 @@ public GetAllPitNodesResponse( ); } + /** + * Copy constructor that explicitly sets the list pit infos + */ + public GetAllPitNodesResponse(List listPitInfos, GetAllPitNodesResponse response) { + super(response.getClusterName(), response.getNodes(), response.failures()); + pitInfos.addAll(listPitInfos); + } + @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(); diff --git a/server/src/main/java/org/opensearch/action/search/NodesGetAllPitsAction.java b/server/src/main/java/org/opensearch/action/search/NodesGetAllPitsAction.java new file mode 100644 index 0000000000000..af41f7d49551c --- /dev/null +++ b/server/src/main/java/org/opensearch/action/search/NodesGetAllPitsAction.java @@ -0,0 +1,23 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.search; + +import org.opensearch.action.ActionType; + +/** + * Action type for retrieving all PIT reader contexts from nodes + */ +public class NodesGetAllPitsAction extends ActionType { + public static final NodesGetAllPitsAction INSTANCE = new NodesGetAllPitsAction(); + public static final String NAME = "cluster:admin/point_in_time/read_from_nodes"; + + private NodesGetAllPitsAction() { + super(NAME, GetAllPitNodesResponse::new); + } +} diff --git a/server/src/main/java/org/opensearch/action/search/PitService.java b/server/src/main/java/org/opensearch/action/search/PitService.java index 0b79b77fd6014..ff068397ad94e 100644 --- a/server/src/main/java/org/opensearch/action/search/PitService.java +++ b/server/src/main/java/org/opensearch/action/search/PitService.java @@ -15,6 +15,7 @@ import org.opensearch.action.ActionListener; import org.opensearch.action.StepListener; import org.opensearch.action.support.GroupedActionListener; +import org.opensearch.client.node.NodeClient; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Strings; @@ -47,12 +48,19 @@ public class PitService { private final ClusterService clusterService; private final SearchTransportService searchTransportService; private final TransportService transportService; + private final NodeClient nodeClient; @Inject - public PitService(ClusterService clusterService, SearchTransportService searchTransportService, TransportService transportService) { + public PitService( + ClusterService clusterService, + SearchTransportService searchTransportService, + TransportService transportService, + NodeClient nodeClient + ) { this.clusterService = clusterService; this.searchTransportService = searchTransportService; this.transportService = transportService; + this.nodeClient = nodeClient; } /** @@ -144,6 +152,17 @@ public void onFailure(final Exception e) { }, size); } + /** + * This method returns indices associated for each pit + */ + public Map getIndicesForPits(List pitIds) { + Map pitToIndicesMap = new HashMap<>(); + for (String pitId : pitIds) { + pitToIndicesMap.put(pitId, SearchContextId.decode(nodeClient.getNamedWriteableRegistry(), pitId).getActualIndices()); + } + return pitToIndicesMap; + } + /** * Get all active point in time contexts */ @@ -156,7 +175,7 @@ public void getAllPits(ActionListener getAllPitsListener DiscoveryNode[] disNodesArr = nodes.toArray(new DiscoveryNode[nodes.size()]); transportService.sendRequest( transportService.getLocalNode(), - GetAllPitsAction.NAME, + NodesGetAllPitsAction.NAME, new GetAllPitNodesRequest(disNodesArr), new TransportResponseHandler() { diff --git a/server/src/main/java/org/opensearch/action/search/TransportDeletePitAction.java b/server/src/main/java/org/opensearch/action/search/TransportDeletePitAction.java index f9e36c479dd54..19abe2361290d 100644 --- a/server/src/main/java/org/opensearch/action/search/TransportDeletePitAction.java +++ b/server/src/main/java/org/opensearch/action/search/TransportDeletePitAction.java @@ -57,7 +57,11 @@ public TransportDeletePitAction( @Override protected void doExecute(Task task, DeletePitRequest request, ActionListener listener) { List pitIds = request.getPitIds(); - if (pitIds.size() == 1 && "_all".equals(pitIds.get(0))) { + // when security plugin intercepts the request, if PITs are not present in the cluster the PIT IDs in request will be empty + // and in this case return empty response + if (pitIds.isEmpty()) { + listener.onResponse(new DeletePitResponse(new ArrayList<>())); + } else if (pitIds.size() == 1 && "_all".equals(pitIds.get(0))) { deleteAllPits(listener); } else { deletePits(listener, request); diff --git a/server/src/main/java/org/opensearch/action/search/TransportGetAllPitsAction.java b/server/src/main/java/org/opensearch/action/search/TransportGetAllPitsAction.java index 21a64e388fa7b..c8529c5b02bd4 100644 --- a/server/src/main/java/org/opensearch/action/search/TransportGetAllPitsAction.java +++ b/server/src/main/java/org/opensearch/action/search/TransportGetAllPitsAction.java @@ -8,79 +8,31 @@ package org.opensearch.action.search; -import org.opensearch.action.FailedNodeException; +import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.nodes.TransportNodesAction; -import org.opensearch.cluster.service.ClusterService; +import org.opensearch.action.support.HandledTransportAction; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.search.SearchService; -import org.opensearch.threadpool.ThreadPool; +import org.opensearch.tasks.Task; import org.opensearch.transport.TransportService; -import java.io.IOException; -import java.util.List; - /** - * Transport action to get all active PIT contexts across all nodes + * Transport action to get all active PIT contexts across the cluster */ -public class TransportGetAllPitsAction extends TransportNodesAction< - GetAllPitNodesRequest, - GetAllPitNodesResponse, - GetAllPitNodeRequest, - GetAllPitNodeResponse> { - private final SearchService searchService; +public class TransportGetAllPitsAction extends HandledTransportAction { + private final PitService pitService; @Inject - public TransportGetAllPitsAction( - ThreadPool threadPool, - ClusterService clusterService, - TransportService transportService, - ActionFilters actionFilters, - SearchService searchService - ) { - super( - GetAllPitsAction.NAME, - threadPool, - clusterService, - transportService, - actionFilters, - GetAllPitNodesRequest::new, - GetAllPitNodeRequest::new, - ThreadPool.Names.SAME, - GetAllPitNodeResponse.class - ); - this.searchService = searchService; - } - - @Override - protected GetAllPitNodesResponse newResponse( - GetAllPitNodesRequest request, - List getAllPitNodeRespons, - List failures - ) { - return new GetAllPitNodesResponse(clusterService.getClusterName(), getAllPitNodeRespons, failures); - } - - @Override - protected GetAllPitNodeRequest newNodeRequest(GetAllPitNodesRequest request) { - return new GetAllPitNodeRequest(); - } - - @Override - protected GetAllPitNodeResponse newNodeResponse(StreamInput in) throws IOException { - return new GetAllPitNodeResponse(in); + public TransportGetAllPitsAction(ActionFilters actionFilters, TransportService transportService, PitService pitService) { + super(GetAllPitsAction.NAME, transportService, actionFilters, in -> new GetAllPitNodesRequest(in)); + this.pitService = pitService; } - /** - * This retrieves all active PITs in the node - */ - @Override - protected GetAllPitNodeResponse nodeOperation(GetAllPitNodeRequest request) { - GetAllPitNodeResponse nodeResponse = new GetAllPitNodeResponse( - transportService.getLocalNode(), - searchService.getAllPITReaderContexts() - ); - return nodeResponse; + protected void doExecute(Task task, GetAllPitNodesRequest request, ActionListener listener) { + // If security plugin intercepts the request, it'll replace all PIT IDs with permitted PIT IDs + if (request.getGetAllPitNodesResponse() != null) { + listener.onResponse(request.getGetAllPitNodesResponse()); + } else { + pitService.getAllPits(listener); + } } } diff --git a/server/src/main/java/org/opensearch/action/search/TransportNodesGetAllPitsAction.java b/server/src/main/java/org/opensearch/action/search/TransportNodesGetAllPitsAction.java new file mode 100644 index 0000000000000..520830cd293f0 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/search/TransportNodesGetAllPitsAction.java @@ -0,0 +1,86 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.search; + +import org.opensearch.action.FailedNodeException; +import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.nodes.TransportNodesAction; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.inject.Inject; +import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.search.SearchService; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportService; + +import java.io.IOException; +import java.util.List; + +/** + * Transport action to get all active PIT contexts across all nodes + */ +public class TransportNodesGetAllPitsAction extends TransportNodesAction< + GetAllPitNodesRequest, + GetAllPitNodesResponse, + GetAllPitNodeRequest, + GetAllPitNodeResponse> { + private final SearchService searchService; + + @Inject + public TransportNodesGetAllPitsAction( + ThreadPool threadPool, + ClusterService clusterService, + TransportService transportService, + ActionFilters actionFilters, + SearchService searchService + ) { + super( + NodesGetAllPitsAction.NAME, + threadPool, + clusterService, + transportService, + actionFilters, + GetAllPitNodesRequest::new, + GetAllPitNodeRequest::new, + ThreadPool.Names.SAME, + GetAllPitNodeResponse.class + ); + this.searchService = searchService; + } + + @Override + protected GetAllPitNodesResponse newResponse( + GetAllPitNodesRequest request, + List getAllPitNodeRespons, + List failures + ) { + return new GetAllPitNodesResponse(clusterService.getClusterName(), getAllPitNodeRespons, failures); + } + + @Override + protected GetAllPitNodeRequest newNodeRequest(GetAllPitNodesRequest request) { + return new GetAllPitNodeRequest(); + } + + @Override + protected GetAllPitNodeResponse newNodeResponse(StreamInput in) throws IOException { + return new GetAllPitNodeResponse(in); + } + + /** + * This retrieves all active PITs in the node + */ + @Override + protected GetAllPitNodeResponse nodeOperation(GetAllPitNodeRequest request) { + GetAllPitNodeResponse nodeResponse = new GetAllPitNodeResponse( + transportService.getLocalNode(), + searchService.getAllPITReaderContexts() + ); + return nodeResponse; + } +} diff --git a/server/src/main/java/org/opensearch/rest/action/search/RestCreatePitAction.java b/server/src/main/java/org/opensearch/rest/action/search/RestCreatePitAction.java new file mode 100644 index 0000000000000..9439670880015 --- /dev/null +++ b/server/src/main/java/org/opensearch/rest/action/search/RestCreatePitAction.java @@ -0,0 +1,57 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.rest.action.search; + +import org.opensearch.action.search.CreatePitRequest; +import org.opensearch.action.support.IndicesOptions; +import org.opensearch.client.node.NodeClient; +import org.opensearch.common.Strings; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.rest.BaseRestHandler; +import org.opensearch.rest.RestRequest; +import org.opensearch.rest.action.RestStatusToXContentListener; + +import java.io.IOException; +import java.util.List; + +import static java.util.Arrays.asList; +import static java.util.Collections.unmodifiableList; +import static org.opensearch.rest.RestRequest.Method.POST; + +/** + * Rest action for creating PIT context + */ +public class RestCreatePitAction extends BaseRestHandler { + public static String ALLOW_PARTIAL_PIT_CREATION = "allow_partial_pit_creation"; + public static String KEEP_ALIVE = "keep_alive"; + + @Override + public String getName() { + return "create_pit_action"; + } + + @Override + public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException { + boolean allowPartialPitCreation = request.paramAsBoolean(ALLOW_PARTIAL_PIT_CREATION, true); + String[] indices = Strings.splitStringByCommaToArray(request.param("index")); + TimeValue keepAlive = request.paramAsTime(KEEP_ALIVE, null); + CreatePitRequest createPitRequest = new CreatePitRequest(keepAlive, allowPartialPitCreation, indices); + createPitRequest.setIndicesOptions(IndicesOptions.fromRequest(request, createPitRequest.indicesOptions())); + createPitRequest.setPreference(request.param("preference")); + createPitRequest.setRouting(request.param("routing")); + + return channel -> client.createPit(createPitRequest, new RestStatusToXContentListener<>(channel)); + } + + @Override + public List routes() { + return unmodifiableList(asList(new Route(POST, "/{index}/_search/point_in_time"))); + } + +} diff --git a/server/src/main/java/org/opensearch/rest/action/search/RestDeletePitAction.java b/server/src/main/java/org/opensearch/rest/action/search/RestDeletePitAction.java new file mode 100644 index 0000000000000..452e66f8f5018 --- /dev/null +++ b/server/src/main/java/org/opensearch/rest/action/search/RestDeletePitAction.java @@ -0,0 +1,60 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.rest.action.search; + +import org.opensearch.action.search.DeletePitRequest; +import org.opensearch.action.search.DeletePitResponse; +import org.opensearch.client.node.NodeClient; +import org.opensearch.rest.BaseRestHandler; +import org.opensearch.rest.RestRequest; +import org.opensearch.rest.action.RestStatusToXContentListener; + +import java.io.IOException; +import java.util.List; + +import static java.util.Arrays.asList; +import static java.util.Collections.unmodifiableList; +import static org.opensearch.rest.RestRequest.Method.DELETE; + +/** + * Rest action for deleting PIT contexts + */ +public class RestDeletePitAction extends BaseRestHandler { + + @Override + public String getName() { + return "delete_pit_action"; + } + + @Override + public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException { + String allPitIdsQualifier = "_all"; + final DeletePitRequest deletePITRequest; + if (request.path().contains(allPitIdsQualifier)) { + deletePITRequest = new DeletePitRequest(asList(allPitIdsQualifier)); + } else { + deletePITRequest = new DeletePitRequest(); + request.withContentOrSourceParamParserOrNull((xContentParser -> { + if (xContentParser != null) { + try { + deletePITRequest.fromXContent(xContentParser); + } catch (IOException e) { + throw new IllegalArgumentException("Failed to parse request body", e); + } + } + })); + } + return channel -> client.deletePits(deletePITRequest, new RestStatusToXContentListener(channel)); + } + + @Override + public List routes() { + return unmodifiableList(asList(new Route(DELETE, "/_search/point_in_time"), new Route(DELETE, "/_search/point_in_time/_all"))); + } +} diff --git a/server/src/main/java/org/opensearch/search/SearchService.java b/server/src/main/java/org/opensearch/search/SearchService.java index 4bd95da193668..04fab85c163a9 100644 --- a/server/src/main/java/org/opensearch/search/SearchService.java +++ b/server/src/main/java/org/opensearch/search/SearchService.java @@ -881,6 +881,7 @@ public void createPitReaderContext(ShardId shardId, TimeValue keepAlive, ActionL shard.awaitShardSearchActive(ignored -> { Engine.SearcherSupplier searcherSupplier = null; ReaderContext readerContext = null; + Releasable decreasePitContexts = openPitContexts::decrementAndGet; try { if (openPitContexts.incrementAndGet() > maxOpenPitContext) { throw new OpenSearchRejectedExecutionException( @@ -902,15 +903,16 @@ public void createPitReaderContext(ShardId shardId, TimeValue keepAlive, ActionL searchOperationListener.onNewPitContext(finalReaderContext); readerContext.addOnClose(() -> { - openPitContexts.decrementAndGet(); searchOperationListener.onFreeReaderContext(finalReaderContext); searchOperationListener.onFreePitContext(finalReaderContext); }); + readerContext.addOnClose(decreasePitContexts); // add the newly created pit reader context to active readers putReaderContext(readerContext); readerContext = null; listener.onResponse(finalReaderContext.id()); } catch (Exception exc) { + Releasables.closeWhileHandlingException(decreasePitContexts); Releasables.closeWhileHandlingException(searcherSupplier, readerContext); listener.onFailure(exc); } diff --git a/server/src/test/java/org/opensearch/action/search/CreatePitControllerTests.java b/server/src/test/java/org/opensearch/action/search/CreatePitControllerTests.java index a5c6e1c12b79c..c03c27f7d7e4d 100644 --- a/server/src/test/java/org/opensearch/action/search/CreatePitControllerTests.java +++ b/server/src/test/java/org/opensearch/action/search/CreatePitControllerTests.java @@ -14,6 +14,7 @@ import org.opensearch.action.ActionListener; import org.opensearch.action.LatchedActionListener; import org.opensearch.action.StepListener; +import org.opensearch.client.node.NodeClient; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; @@ -70,6 +71,8 @@ public class CreatePitControllerTests extends OpenSearchTestCase { ClusterService clusterServiceMock = null; private final ThreadPool threadPool = new TestThreadPool(getClass().getName()); + Settings settings = Settings.builder().put("node.name", CreatePitControllerTests.class.getSimpleName()).build(); + NodeClient client = new NodeClient(settings, threadPool); @Override public void tearDown() throws Exception { @@ -219,7 +222,7 @@ public Transport.Connection getConnection(String clusterAlias, DiscoveryNode nod CreatePitRequest request = new CreatePitRequest(TimeValue.timeValueDays(1), true); request.setIndices(new String[] { "index" }); - PitService pitService = new PitService(clusterServiceMock, searchTransportService, transportService); + PitService pitService = new PitService(clusterServiceMock, searchTransportService, transportService, client); CreatePitController controller = new CreatePitController( searchTransportService, clusterServiceMock, @@ -308,7 +311,7 @@ public void sendFreePITContexts( CreatePitRequest request = new CreatePitRequest(TimeValue.timeValueDays(1), true); request.setIndices(new String[] { "index" }); - PitService pitService = new PitService(clusterServiceMock, searchTransportService, transportService); + PitService pitService = new PitService(clusterServiceMock, searchTransportService, transportService, client); CreatePitController controller = new CreatePitController( searchTransportService, clusterServiceMock, @@ -406,7 +409,7 @@ public Transport.Connection getConnection(String clusterAlias, DiscoveryNode nod CreatePitRequest request = new CreatePitRequest(TimeValue.timeValueDays(1), true); request.setIndices(new String[] { "index" }); - PitService pitService = new PitService(clusterServiceMock, searchTransportService, transportService); + PitService pitService = new PitService(clusterServiceMock, searchTransportService, transportService, client); CreatePitController controller = new CreatePitController( searchTransportService, clusterServiceMock, @@ -494,7 +497,7 @@ public Transport.Connection getConnection(String clusterAlias, DiscoveryNode nod }; CreatePitRequest request = new CreatePitRequest(TimeValue.timeValueDays(1), true); request.setIndices(new String[] { "index" }); - PitService pitService = new PitService(clusterServiceMock, searchTransportService, transportService); + PitService pitService = new PitService(clusterServiceMock, searchTransportService, transportService, client); CreatePitController controller = new CreatePitController( searchTransportService, clusterServiceMock, diff --git a/server/src/test/java/org/opensearch/action/search/TransportDeletePitActionTests.java b/server/src/test/java/org/opensearch/action/search/TransportDeletePitActionTests.java index 7a1d9a6fe963c..bdc0440a89f69 100644 --- a/server/src/test/java/org/opensearch/action/search/TransportDeletePitActionTests.java +++ b/server/src/test/java/org/opensearch/action/search/TransportDeletePitActionTests.java @@ -13,6 +13,7 @@ import org.opensearch.action.support.ActionFilter; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.PlainActionFuture; +import org.opensearch.client.node.NodeClient; import org.opensearch.cluster.ClusterName; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.metadata.Metadata; @@ -62,6 +63,7 @@ public class TransportDeletePitActionTests extends OpenSearchTestCase { ClusterService clusterServiceMock = null; Settings settings = Settings.builder().put("node.name", TransportMultiSearchActionTests.class.getSimpleName()).build(); private ThreadPool threadPool = new ThreadPool(settings); + NodeClient client = new NodeClient(settings, threadPool); @Override public void tearDown() throws Exception { @@ -165,7 +167,7 @@ public Transport.Connection getConnection(String clusterAlias, DiscoveryNode nod return new SearchAsyncActionTests.MockConnection(node); } }; - PitService pitService = new PitService(clusterServiceMock, searchTransportService, transportService); + PitService pitService = new PitService(clusterServiceMock, searchTransportService, transportService, client); TransportDeletePitAction action = new TransportDeletePitAction( transportService, actionFilters, @@ -229,7 +231,7 @@ public Transport.Connection getConnection(String clusterAlias, DiscoveryNode nod return new SearchAsyncActionTests.MockConnection(node); } }; - PitService pitService = new PitService(clusterServiceMock, searchTransportService, transportService) { + PitService pitService = new PitService(clusterServiceMock, searchTransportService, transportService, client) { @Override public void getAllPits(ActionListener getAllPitsListener) { ListPitInfo listPitInfo = new ListPitInfo(getPitId(), 0, 0); @@ -312,7 +314,7 @@ public Transport.Connection getConnection(String clusterAlias, DiscoveryNode nod return new SearchAsyncActionTests.MockConnection(node); } }; - PitService pitService = new PitService(clusterServiceMock, searchTransportService, transportService); + PitService pitService = new PitService(clusterServiceMock, searchTransportService, transportService, client); TransportDeletePitAction action = new TransportDeletePitAction( transportService, actionFilters, @@ -371,7 +373,7 @@ public Transport.Connection getConnection(String clusterAlias, DiscoveryNode nod return new SearchAsyncActionTests.MockConnection(node); } }; - PitService pitService = new PitService(clusterServiceMock, searchTransportService, transportService); + PitService pitService = new PitService(clusterServiceMock, searchTransportService, transportService, client); TransportDeletePitAction action = new TransportDeletePitAction( transportService, actionFilters, @@ -439,7 +441,7 @@ public Transport.Connection getConnection(String clusterAlias, DiscoveryNode nod return new SearchAsyncActionTests.MockConnection(node); } }; - PitService pitService = new PitService(clusterServiceMock, searchTransportService, transportService); + PitService pitService = new PitService(clusterServiceMock, searchTransportService, transportService, client); TransportDeletePitAction action = new TransportDeletePitAction( transportService, actionFilters, @@ -505,7 +507,7 @@ public Transport.Connection getConnection(String clusterAlias, DiscoveryNode nod return new SearchAsyncActionTests.MockConnection(node); } }; - PitService pitService = new PitService(clusterServiceMock, searchTransportService, transportService) { + PitService pitService = new PitService(clusterServiceMock, searchTransportService, transportService, client) { @Override public void getAllPits(ActionListener getAllPitsListener) { ListPitInfo listPitInfo = new ListPitInfo(getPitId(), 0, 0); @@ -581,7 +583,7 @@ public Transport.Connection getConnection(String clusterAlias, DiscoveryNode nod return new SearchAsyncActionTests.MockConnection(node); } }; - PitService pitService = new PitService(clusterServiceMock, searchTransportService, transportService) { + PitService pitService = new PitService(clusterServiceMock, searchTransportService, transportService, client) { @Override public void getAllPits(ActionListener getAllPitsListener) { ListPitInfo listPitInfo = new ListPitInfo(getPitId(), 0, 0); @@ -661,7 +663,7 @@ public Transport.Connection getConnection(String clusterAlias, DiscoveryNode nod return new SearchAsyncActionTests.MockConnection(node); } }; - PitService pitService = new PitService(clusterServiceMock, searchTransportService, transportService) { + PitService pitService = new PitService(clusterServiceMock, searchTransportService, transportService, client) { @Override public void getAllPits(ActionListener getAllPitsListener) { ListPitInfo listPitInfo = new ListPitInfo(getPitId(), 0, 0); diff --git a/server/src/test/java/org/opensearch/search/CreatePitSingleNodeTests.java b/server/src/test/java/org/opensearch/search/CreatePitSingleNodeTests.java index a10f004b2ee97..9a28f1800847e 100644 --- a/server/src/test/java/org/opensearch/search/CreatePitSingleNodeTests.java +++ b/server/src/test/java/org/opensearch/search/CreatePitSingleNodeTests.java @@ -14,6 +14,10 @@ import org.opensearch.action.search.CreatePitController; import org.opensearch.action.search.CreatePitRequest; import org.opensearch.action.search.CreatePitResponse; +import org.opensearch.action.search.DeletePitAction; +import org.opensearch.action.search.DeletePitInfo; +import org.opensearch.action.search.DeletePitRequest; +import org.opensearch.action.search.DeletePitResponse; import org.opensearch.action.search.PitTestsUtil; import org.opensearch.action.search.SearchPhaseExecutionException; import org.opensearch.action.search.SearchResponse; @@ -33,6 +37,8 @@ import java.util.concurrent.ExecutionException; import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.Matchers.blankOrNullString; +import static org.hamcrest.Matchers.not; import static org.opensearch.action.search.PitTestsUtil.assertSegments; import static org.opensearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; @@ -282,6 +288,52 @@ public void testMaxOpenPitContexts() throws Exception { validatePitStats("index", 0, maxPitContexts, 0); } + public void testCreatePitMoreThanMaxOpenPitContexts() throws Exception { + createIndex("index"); + client().prepareIndex("index").setId("1").setSource("field", "value").setRefreshPolicy(IMMEDIATE).get(); + + CreatePitRequest request = new CreatePitRequest(TimeValue.timeValueDays(1), true); + request.setIndices(new String[] { "index" }); + SearchService service = getInstanceFromNode(SearchService.class); + + try { + for (int i = 0; i < 1000; i++) { + client().execute(CreatePitAction.INSTANCE, request).get(); + } + } catch (Exception ex) { + assertTrue( + ex.getMessage() + .contains( + "Trying to create too many Point In Time contexts. " + + "Must be less than or equal to: [" + + SearchService.MAX_OPEN_PIT_CONTEXT.get(Settings.EMPTY) + + "]. " + + "This limit can be set by changing the [search.max_open_pit_context] setting." + ) + ); + } + final int maxPitContexts = SearchService.MAX_OPEN_PIT_CONTEXT.get(Settings.EMPTY); + validatePitStats("index", maxPitContexts, 0, 0); + // deleteall + DeletePitRequest deletePITRequest = new DeletePitRequest("_all"); + + /** + * When we invoke delete again, returns success after clearing the remaining readers. Asserting reader context + * not found exceptions don't result in failures ( as deletion in one node is successful ) + */ + ActionFuture execute = client().execute(DeletePitAction.INSTANCE, deletePITRequest); + DeletePitResponse deletePITResponse = execute.get(); + for (DeletePitInfo deletePitInfo : deletePITResponse.getDeletePitResults()) { + assertThat(deletePitInfo.getPitId(), not(blankOrNullString())); + assertTrue(deletePitInfo.isSuccessful()); + } + validatePitStats("index", 0, maxPitContexts, 0); + client().execute(CreatePitAction.INSTANCE, request).get(); + validatePitStats("index", 1, maxPitContexts, 0); + service.doClose(); + validatePitStats("index", 0, maxPitContexts + 1, 0); + } + public void testOpenPitContextsConcurrently() throws Exception { createIndex("index"); final int maxPitContexts = SearchService.MAX_OPEN_PIT_CONTEXT.get(Settings.EMPTY); diff --git a/server/src/test/java/org/opensearch/search/pit/RestCreatePitActionTests.java b/server/src/test/java/org/opensearch/search/pit/RestCreatePitActionTests.java new file mode 100644 index 0000000000000..5ca384daedbff --- /dev/null +++ b/server/src/test/java/org/opensearch/search/pit/RestCreatePitActionTests.java @@ -0,0 +1,78 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.search.pit; + +import org.apache.lucene.util.SetOnce; +import org.opensearch.action.ActionListener; +import org.opensearch.action.search.CreatePitRequest; +import org.opensearch.action.search.CreatePitResponse; +import org.opensearch.client.node.NodeClient; +import org.opensearch.rest.RestRequest; +import org.opensearch.rest.action.search.RestCreatePitAction; +import org.opensearch.test.OpenSearchTestCase; +import org.opensearch.test.client.NoOpNodeClient; +import org.opensearch.test.rest.FakeRestChannel; +import org.opensearch.test.rest.FakeRestRequest; + +import java.util.HashMap; +import java.util.Map; + +import static org.hamcrest.Matchers.equalTo; + +/** + * Tests to verify behavior of create pit rest action + */ +public class RestCreatePitActionTests extends OpenSearchTestCase { + public void testRestCreatePit() throws Exception { + SetOnce createPitCalled = new SetOnce<>(); + RestCreatePitAction action = new RestCreatePitAction(); + try (NodeClient nodeClient = new NoOpNodeClient(this.getTestName()) { + @Override + public void createPit(CreatePitRequest request, ActionListener listener) { + createPitCalled.set(true); + assertThat(request.getKeepAlive().getStringRep(), equalTo("1m")); + assertFalse(request.shouldAllowPartialPitCreation()); + } + }) { + Map params = new HashMap<>(); + params.put("keep_alive", "1m"); + params.put("allow_partial_pit_creation", "false"); + RestRequest request = new FakeRestRequest.Builder(xContentRegistry()).withParams(params) + .withMethod(RestRequest.Method.POST) + .build(); + FakeRestChannel channel = new FakeRestChannel(request, false, 0); + action.handleRequest(request, channel, nodeClient); + + assertThat(createPitCalled.get(), equalTo(true)); + } + } + + public void testRestCreatePitDefaultPartialCreation() throws Exception { + SetOnce createPitCalled = new SetOnce<>(); + RestCreatePitAction action = new RestCreatePitAction(); + try (NodeClient nodeClient = new NoOpNodeClient(this.getTestName()) { + @Override + public void createPit(CreatePitRequest request, ActionListener listener) { + createPitCalled.set(true); + assertThat(request.getKeepAlive().getStringRep(), equalTo("1m")); + assertTrue(request.shouldAllowPartialPitCreation()); + } + }) { + Map params = new HashMap<>(); + params.put("keep_alive", "1m"); + RestRequest request = new FakeRestRequest.Builder(xContentRegistry()).withParams(params) + .withMethod(RestRequest.Method.POST) + .build(); + FakeRestChannel channel = new FakeRestChannel(request, false, 0); + action.handleRequest(request, channel, nodeClient); + + assertThat(createPitCalled.get(), equalTo(true)); + } + } +} diff --git a/server/src/test/java/org/opensearch/search/pit/RestDeletePitActionTests.java b/server/src/test/java/org/opensearch/search/pit/RestDeletePitActionTests.java new file mode 100644 index 0000000000000..0bfa16aafe1e3 --- /dev/null +++ b/server/src/test/java/org/opensearch/search/pit/RestDeletePitActionTests.java @@ -0,0 +1,133 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.search.pit; + +import org.apache.lucene.util.SetOnce; +import org.opensearch.action.ActionListener; +import org.opensearch.action.search.DeletePitRequest; +import org.opensearch.action.search.DeletePitResponse; +import org.opensearch.client.node.NodeClient; +import org.opensearch.common.bytes.BytesArray; +import org.opensearch.common.xcontent.XContentType; +import org.opensearch.rest.RestRequest; +import org.opensearch.rest.action.search.RestDeletePitAction; +import org.opensearch.test.OpenSearchTestCase; +import org.opensearch.test.client.NoOpNodeClient; +import org.opensearch.test.rest.FakeRestChannel; +import org.opensearch.test.rest.FakeRestRequest; + +import java.util.Collections; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; + +/** + * Tests to verify the behavior of rest delete pit action for list delete and delete all PIT endpoints + */ +public class RestDeletePitActionTests extends OpenSearchTestCase { + public void testParseDeletePitRequestWithInvalidJsonThrowsException() throws Exception { + RestDeletePitAction action = new RestDeletePitAction(); + RestRequest request = new FakeRestRequest.Builder(xContentRegistry()).withContent( + new BytesArray("{invalid_json}"), + XContentType.JSON + ).build(); + Exception e = expectThrows(IllegalArgumentException.class, () -> action.prepareRequest(request, null)); + assertThat(e.getMessage(), equalTo("Failed to parse request body")); + } + + public void testDeletePitWithBody() throws Exception { + SetOnce pitCalled = new SetOnce<>(); + try (NodeClient nodeClient = new NoOpNodeClient(this.getTestName()) { + @Override + public void deletePits(DeletePitRequest request, ActionListener listener) { + pitCalled.set(true); + assertThat(request.getPitIds(), hasSize(1)); + assertThat(request.getPitIds().get(0), equalTo("BODY")); + } + }) { + RestDeletePitAction action = new RestDeletePitAction(); + RestRequest request = new FakeRestRequest.Builder(xContentRegistry()).withContent( + new BytesArray("{\"pit_id\": [\"BODY\"]}"), + XContentType.JSON + ).build(); + FakeRestChannel channel = new FakeRestChannel(request, false, 0); + action.handleRequest(request, channel, nodeClient); + + assertThat(pitCalled.get(), equalTo(true)); + } + } + + public void testDeleteAllPit() throws Exception { + SetOnce pitCalled = new SetOnce<>(); + try (NodeClient nodeClient = new NoOpNodeClient(this.getTestName()) { + @Override + public void deletePits(DeletePitRequest request, ActionListener listener) { + pitCalled.set(true); + assertThat(request.getPitIds(), hasSize(1)); + assertThat(request.getPitIds().get(0), equalTo("_all")); + } + }) { + RestDeletePitAction action = new RestDeletePitAction(); + RestRequest request = new FakeRestRequest.Builder(xContentRegistry()).withPath("/_all").build(); + FakeRestChannel channel = new FakeRestChannel(request, false, 0); + action.handleRequest(request, channel, nodeClient); + + assertThat(pitCalled.get(), equalTo(true)); + } + } + + public void testDeleteAllPitWithBody() { + SetOnce pitCalled = new SetOnce<>(); + try (NodeClient nodeClient = new NoOpNodeClient(this.getTestName()) { + @Override + public void deletePits(DeletePitRequest request, ActionListener listener) { + pitCalled.set(true); + assertThat(request.getPitIds(), hasSize(1)); + assertThat(request.getPitIds().get(0), equalTo("_all")); + } + }) { + RestDeletePitAction action = new RestDeletePitAction(); + RestRequest request = new FakeRestRequest.Builder(xContentRegistry()).withContent( + new BytesArray("{\"pit_id\": [\"BODY\"]}"), + XContentType.JSON + ).withPath("/_all").build(); + FakeRestChannel channel = new FakeRestChannel(request, false, 0); + + IllegalArgumentException ex = expectThrows( + IllegalArgumentException.class, + () -> action.handleRequest(request, channel, nodeClient) + ); + assertTrue(ex.getMessage().contains("request [GET /_all] does not support having a body")); + } + } + + public void testDeletePitQueryStringParamsShouldThrowException() { + SetOnce pitCalled = new SetOnce<>(); + try (NodeClient nodeClient = new NoOpNodeClient(this.getTestName()) { + @Override + public void deletePits(DeletePitRequest request, ActionListener listener) { + pitCalled.set(true); + assertThat(request.getPitIds(), hasSize(2)); + assertThat(request.getPitIds().get(0), equalTo("QUERY_STRING")); + assertThat(request.getPitIds().get(1), equalTo("QUERY_STRING_1")); + } + }) { + RestDeletePitAction action = new RestDeletePitAction(); + RestRequest request = new FakeRestRequest.Builder(xContentRegistry()).withParams( + Collections.singletonMap("pit_id", "QUERY_STRING,QUERY_STRING_1") + ).build(); + FakeRestChannel channel = new FakeRestChannel(request, false, 0); + IllegalArgumentException ex = expectThrows( + IllegalArgumentException.class, + () -> action.handleRequest(request, channel, nodeClient) + ); + assertTrue(ex.getMessage().contains("unrecognized param")); + } + } +} From 5602013852779092fd33b846755f66249a7c7fe0 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Thu, 1 Sep 2022 14:31:45 +0530 Subject: [PATCH 073/187] skip prevoting for decommissioned nodes irrespective of cluster state --- .../cluster/coordination/Coordinator.java | 5 +++++ .../java/org/opensearch/discovery/PeerFinder.java | 13 ++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/server/src/main/java/org/opensearch/cluster/coordination/Coordinator.java b/server/src/main/java/org/opensearch/cluster/coordination/Coordinator.java index cb99f02bbf03f..dd928dd911304 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/Coordinator.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/Coordinator.java @@ -1439,6 +1439,11 @@ private void startElectionScheduler() { public void run() { synchronized (mutex) { if (mode == Mode.CANDIDATE) { + if(peerFinder.localNodeDecommissioned()) { + logger.debug("skip prevoting as local node is decommissioned"); + return; + } + final ClusterState lastAcceptedState = coordinationState.get().getLastAcceptedState(); if (localNodeMayWinElection(lastAcceptedState) == false) { diff --git a/server/src/main/java/org/opensearch/discovery/PeerFinder.java b/server/src/main/java/org/opensearch/discovery/PeerFinder.java index 5f89e681f5526..0cd7169ff191e 100644 --- a/server/src/main/java/org/opensearch/discovery/PeerFinder.java +++ b/server/src/main/java/org/opensearch/discovery/PeerFinder.java @@ -110,6 +110,7 @@ public abstract class PeerFinder { private volatile long currentTerm; private boolean active; + private boolean localNodeDecommissioned = false; private DiscoveryNodes lastAcceptedNodes; private final Map peersByAddress = new LinkedHashMap<>(); private Optional leader = Optional.empty(); @@ -143,17 +144,27 @@ public ActionListener nodeCommissionedListener() { @Override public void onResponse(Void unused) { logger.info("setting findPeersInterval to [{}], due to recommissioning", findPeersInterval); + assert localNodeDecommissioned; // TODO: Do we need this? + localNodeDecommissioned = false; findPeersInterval = DISCOVERY_FIND_PEERS_INTERVAL_SETTING.get(settings); + } @Override public void onFailure(Exception e) { - logger.info("setting findPeersInterval to [{}], due to decommissioning", findPeersInterval); + logger.info("setting findPeersInterval to [{}], due to decommissioning", + DISCOVERY_FIND_PEERS_INTERVAL_DURING_DECOMMISSION_SETTING.get(settings)); + assert !localNodeDecommissioned; + localNodeDecommissioned = true; findPeersInterval = DISCOVERY_FIND_PEERS_INTERVAL_DURING_DECOMMISSION_SETTING.get(settings); } }; } + public boolean localNodeDecommissioned() { + return localNodeDecommissioned; + } + public void activate(final DiscoveryNodes lastAcceptedNodes) { logger.trace("activating with {}", lastAcceptedNodes); From e9a48d015b07a1f461b4dc51338a7b4ebf04f8f2 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Thu, 18 Aug 2022 16:17:48 +0530 Subject: [PATCH 074/187] Add APIs (GET/PUT) to decommission awareness attribute Signed-off-by: Rishab Nahata --- ...cluster.delete_decommission_awareness.json | 35 ++++++ .../cluster.get_decommission_awareness.json | 29 +++++ .../cluster.put_decommission_awareness.json | 35 ++++++ .../org/opensearch/action/ActionModule.java | 7 ++ .../awareness/put/PutDecommissionAction.java | 20 ++++ .../awareness/put/PutDecommissionRequest.java | 106 +++++++++++++++++ .../put/PutDecommissionRequestBuilder.java | 43 +++++++ .../put/PutDecommissionResponse.java | 32 +++++ .../put/TransportPutDecommissionAction.java | 85 ++++++++++++++ .../opensearch/client/ClusterAdminClient.java | 18 +++ .../java/org/opensearch/client/Requests.java | 8 ++ .../client/support/AbstractClient.java | 19 +++ .../decommission/DecommissionAttribute.java | 111 ++++++++++++++++++ .../cluster/RestPutDecommissionAction.java | 65 ++++++++++ 14 files changed, 613 insertions(+) create mode 100644 rest-api-spec/src/main/resources/rest-api-spec/api/cluster.delete_decommission_awareness.json create mode 100644 rest-api-spec/src/main/resources/rest-api-spec/api/cluster.get_decommission_awareness.json create mode 100644 rest-api-spec/src/main/resources/rest-api-spec/api/cluster.put_decommission_awareness.json create mode 100644 server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionAction.java create mode 100644 server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequest.java create mode 100644 server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequestBuilder.java create mode 100644 server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionResponse.java create mode 100644 server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/TransportPutDecommissionAction.java create mode 100644 server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java create mode 100644 server/src/main/java/org/opensearch/rest/action/admin/cluster/RestPutDecommissionAction.java diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.delete_decommission_awareness.json b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.delete_decommission_awareness.json new file mode 100644 index 0000000000000..a28eba8429fd0 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.delete_decommission_awareness.json @@ -0,0 +1,35 @@ +{ + "cluster.delete_decommission_awareness": { + "documentation": { + "url": "TBA", + "description": "TBA" + }, + "stability": "experimental", + "url": { + "paths": [ + { + "path": "/_cluster/decommission/awareness/{awareness_attribute_name}/{awareness_attribute_value}", + "methods": [ + "DELETE" + ], + "parts": { + "awareness_attribute_name": { + "type": "string", + "description": "Awareness attribute name" + }, + "awareness_attribute_value": { + "type": "string", + "description": "Awareness attribute value" + } + } + } + ] + }, + "params": { + "timeout": { + "type": "time", + "description": "Explicit operation timeout" + } + } + } +} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.get_decommission_awareness.json b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.get_decommission_awareness.json new file mode 100644 index 0000000000000..b5aa1787fd87c --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.get_decommission_awareness.json @@ -0,0 +1,29 @@ +{ + "cluster.get_decommission_awareness": { + "documentation": { + "url": "TBA", + "description": "TBA" + }, + "stability": "experimental", + "url": { + "paths": [ + { + "path": "/_cluster/decommission/awareness/_status", + "methods": [ + "GET" + ] + } + ] + }, + "params": { + "timeout": { + "type": "time", + "description": "Explicit operation timeout" + }, + "local": { + "type": "boolean", + "description": "Return local information, do not retrieve the state from master node (default: false)" + } + } + } +} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.put_decommission_awareness.json b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.put_decommission_awareness.json new file mode 100644 index 0000000000000..3dfd0f50851f3 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.put_decommission_awareness.json @@ -0,0 +1,35 @@ +{ + "cluster.put_decommission_awareness": { + "documentation": { + "url": "TBA", + "description": "TBA" + }, + "stability": "experimental", + "url": { + "paths": [ + { + "path": "/_cluster/decommission/awareness/{awareness_attribute_name}/{awareness_attribute_value}", + "methods": [ + "PUT" + ], + "parts": { + "awareness_attribute_name": { + "type": "string", + "description": "Awareness attribute name" + }, + "awareness_attribute_value": { + "type": "string", + "description": "Awareness attribute value" + } + } + } + ] + }, + "params": { + "timeout": { + "type": "time", + "description": "Explicit operation timeout" + } + } + } +} diff --git a/server/src/main/java/org/opensearch/action/ActionModule.java b/server/src/main/java/org/opensearch/action/ActionModule.java index 74be544123d9f..5f53b0f1074bf 100644 --- a/server/src/main/java/org/opensearch/action/ActionModule.java +++ b/server/src/main/java/org/opensearch/action/ActionModule.java @@ -40,6 +40,8 @@ import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsAction; import org.opensearch.action.admin.cluster.configuration.TransportAddVotingConfigExclusionsAction; import org.opensearch.action.admin.cluster.configuration.TransportClearVotingConfigExclusionsAction; +import org.opensearch.action.admin.cluster.decommission.awareness.put.PutDecommissionAction; +import org.opensearch.action.admin.cluster.decommission.awareness.put.TransportPutDecommissionAction; import org.opensearch.action.admin.cluster.health.ClusterHealthAction; import org.opensearch.action.admin.cluster.health.TransportClusterHealthAction; import org.opensearch.action.admin.cluster.node.hotthreads.NodesHotThreadsAction; @@ -317,6 +319,7 @@ import org.opensearch.rest.action.admin.cluster.RestNodesStatsAction; import org.opensearch.rest.action.admin.cluster.RestNodesUsageAction; import org.opensearch.rest.action.admin.cluster.RestPendingClusterTasksAction; +import org.opensearch.rest.action.admin.cluster.RestPutDecommissionAction; import org.opensearch.rest.action.admin.cluster.RestPutRepositoryAction; import org.opensearch.rest.action.admin.cluster.RestPutStoredScriptAction; import org.opensearch.rest.action.admin.cluster.RestReloadSecureSettingsAction; @@ -683,6 +686,9 @@ public void reg // Remote Store actions.register(RestoreRemoteStoreAction.INSTANCE, TransportRestoreRemoteStoreAction.class); + // Decommission actions + actions.register(PutDecommissionAction.INSTANCE, TransportPutDecommissionAction.class); + return unmodifiableMap(actions.getRegistry()); } @@ -873,6 +879,7 @@ public void initRestHandlers(Supplier nodesInCluster) { } } registerHandler.accept(new RestCatAction(catActions)); + registerHandler.accept(new RestPutDecommissionAction()); // Remote Store APIs if (FeatureFlags.isEnabled(FeatureFlags.REMOTE_STORE)) { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionAction.java new file mode 100644 index 0000000000000..5e033f7d0b355 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionAction.java @@ -0,0 +1,20 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.decommission.awareness.put; + +import org.opensearch.action.ActionType; + +public final class PutDecommissionAction extends ActionType { + public static final PutDecommissionAction INSTANCE = new PutDecommissionAction(); + public static final String NAME = "cluster:admin/decommission/awareness/put"; + + private PutDecommissionAction() { + super(NAME, PutDecommissionResponse::new); + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequest.java new file mode 100644 index 0000000000000..fcbc77727b5c8 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequest.java @@ -0,0 +1,106 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.decommission.awareness.put; + +import org.opensearch.action.ActionRequestValidationException; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeRequest; +import org.opensearch.cluster.decommission.DecommissionAttribute; +import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.common.unit.TimeValue; + +import java.io.IOException; + +import static org.opensearch.action.ValidateActions.addValidationError; + +public class PutDecommissionRequest extends ClusterManagerNodeRequest { + + private DecommissionAttribute decommissionAttribute; + private TimeValue timeout; + + public PutDecommissionRequest() { + } + + public PutDecommissionRequest(DecommissionAttribute decommissionAttribute, TimeValue timeout) { + this.decommissionAttribute = decommissionAttribute; + this.timeout = timeout; + } + + public PutDecommissionRequest(DecommissionAttribute decommissionAttribute) { + this.decommissionAttribute = decommissionAttribute; + } + + public PutDecommissionRequest(StreamInput in) throws IOException { + super(in); + decommissionAttribute = new DecommissionAttribute(in); + timeout = in.readTimeValue(); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + decommissionAttribute.writeTo(out); + out.writeTimeValue(timeout); + } + + /** + * Sets decommission attribute for decommission request + * + * @param decommissionAttribute attribute key-value that needs to be decommissioned + * @return this request + */ + public PutDecommissionRequest setDecommissionAttribute(DecommissionAttribute decommissionAttribute) { + this.decommissionAttribute = decommissionAttribute; + return this; + } + + /** + * Sets the timeout for the request + * + * @param timeout time out for the request + * @return this request + */ + public PutDecommissionRequest setTimeout(TimeValue timeout) { + this.timeout = timeout; + return this; + } + + /** + * @return Returns the decommission attribute key-value + */ + public DecommissionAttribute getDecommissionAttribute() { + return this.decommissionAttribute; + } + + public TimeValue getTimeout() { + return this.timeout; + } + + @Override + public ActionRequestValidationException validate() { + ActionRequestValidationException validationException = null; + if (decommissionAttribute.attributeName() == null) { + validationException = addValidationError("attribute name is missing", validationException); + } + if (decommissionAttribute.attributeValue() == null) { + validationException = addValidationError("attribute value is missing", validationException); + } + return validationException; + } + + @Override + public String toString() { + return "PutDecommissionRequest{" + + "decommissionAttribute=" + + decommissionAttribute + + ", timeout=" + + timeout + + '}'; + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequestBuilder.java new file mode 100644 index 0000000000000..e772cd33a42b2 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequestBuilder.java @@ -0,0 +1,43 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.decommission.awareness.put; + +import org.opensearch.action.ActionType; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeOperationRequestBuilder; +import org.opensearch.client.OpenSearchClient; +import org.opensearch.cluster.decommission.DecommissionAttribute; +import org.opensearch.common.unit.TimeValue; + +public class PutDecommissionRequestBuilder extends ClusterManagerNodeOperationRequestBuilder< + PutDecommissionRequest, + PutDecommissionResponse, + PutDecommissionRequestBuilder> { + + public PutDecommissionRequestBuilder(OpenSearchClient client, ActionType action, PutDecommissionRequest request) { + super(client, action, request); + } + + /** + * @param decommissionAttribute decommission attribute + * @return current object + */ + public PutDecommissionRequestBuilder setDecommissionedAttribute(DecommissionAttribute decommissionAttribute) { + request.setDecommissionAttribute(decommissionAttribute); + return this; + } + + /** + * @param timeout time out for the request + * @return current object + */ + public PutDecommissionRequestBuilder setTimeout(TimeValue timeout) { + request.setTimeout(timeout); + return this; + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionResponse.java new file mode 100644 index 0000000000000..695e894aaa886 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionResponse.java @@ -0,0 +1,32 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.decommission.awareness.put; + +import org.opensearch.action.support.master.AcknowledgedResponse; +import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.common.xcontent.ToXContentObject; + +import java.io.IOException; + +public class PutDecommissionResponse extends AcknowledgedResponse implements ToXContentObject { + + public PutDecommissionResponse(StreamInput in) throws IOException { + super(in); + } + + public PutDecommissionResponse(boolean acknowledged) { + super(acknowledged); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/TransportPutDecommissionAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/TransportPutDecommissionAction.java new file mode 100644 index 0000000000000..e7a37853ef9bd --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/TransportPutDecommissionAction.java @@ -0,0 +1,85 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.decommission.awareness.put; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.action.ActionListener; +import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.block.ClusterBlockException; +import org.opensearch.cluster.block.ClusterBlockLevel; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.inject.Inject; +import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportService; + +import java.io.IOException; + +public class TransportPutDecommissionAction extends TransportClusterManagerNodeAction< + PutDecommissionRequest, + PutDecommissionResponse> { + + private static final Logger logger = LogManager.getLogger(TransportPutDecommissionAction.class); + + @Inject + public TransportPutDecommissionAction( + TransportService transportService, + ClusterService clusterService, +// DecommissionService decommissionService, + ThreadPool threadPool, + ActionFilters actionFilters, + IndexNameExpressionResolver indexNameExpressionResolver + ) { + super( + PutDecommissionAction.NAME, + transportService, + clusterService, + threadPool, + actionFilters, + PutDecommissionRequest::new, + indexNameExpressionResolver + ); + //TODO - uncomment when integrating with the service +// this.decommissionService = decommissionService; + } + + @Override + protected String executor() { + return ThreadPool.Names.SAME; + } + + @Override + protected PutDecommissionResponse read(StreamInput in) throws IOException { + return new PutDecommissionResponse(in); + } + + @Override + protected ClusterBlockException checkBlock(PutDecommissionRequest request, ClusterState state) { + return state.blocks().globalBlockedException(ClusterBlockLevel.METADATA_WRITE); + } + + @Override + protected void clusterManagerOperation( + PutDecommissionRequest request, + ClusterState state, + ActionListener listener + ) throws Exception { + logger.info("initiating awareness attribute [{}] decommissioning", request.getDecommissionAttribute().toString()); + // TODO - uncomment when integrating with the service +// decommissionService.initiateAttributeDecommissioning( +// request.getDecommissionAttribute(), +// listener, +// state +// ); + } +} diff --git a/server/src/main/java/org/opensearch/client/ClusterAdminClient.java b/server/src/main/java/org/opensearch/client/ClusterAdminClient.java index 7a7b98bf724f6..b8536f0fabbaf 100644 --- a/server/src/main/java/org/opensearch/client/ClusterAdminClient.java +++ b/server/src/main/java/org/opensearch/client/ClusterAdminClient.java @@ -37,6 +37,9 @@ import org.opensearch.action.admin.cluster.allocation.ClusterAllocationExplainRequest; import org.opensearch.action.admin.cluster.allocation.ClusterAllocationExplainRequestBuilder; import org.opensearch.action.admin.cluster.allocation.ClusterAllocationExplainResponse; +import org.opensearch.action.admin.cluster.decommission.awareness.put.PutDecommissionRequest; +import org.opensearch.action.admin.cluster.decommission.awareness.put.PutDecommissionRequestBuilder; +import org.opensearch.action.admin.cluster.decommission.awareness.put.PutDecommissionResponse; import org.opensearch.action.admin.cluster.health.ClusterHealthRequest; import org.opensearch.action.admin.cluster.health.ClusterHealthRequestBuilder; import org.opensearch.action.admin.cluster.health.ClusterHealthResponse; @@ -791,4 +794,19 @@ public interface ClusterAdminClient extends OpenSearchClient { * Delete specified dangling indices. */ ActionFuture deleteDanglingIndex(DeleteDanglingIndexRequest request); + + /** + * Decommission a node + */ + ActionFuture putDecommission(PutDecommissionRequest request); + + /** + * Decommission a node + */ + void putDecommission(PutDecommissionRequest request, ActionListener listener); + + /** + * Decommission a node + */ + PutDecommissionRequestBuilder preparePutDecommission(PutDecommissionRequest request); } diff --git a/server/src/main/java/org/opensearch/client/Requests.java b/server/src/main/java/org/opensearch/client/Requests.java index b04de7830a780..44b8b9e210bf7 100644 --- a/server/src/main/java/org/opensearch/client/Requests.java +++ b/server/src/main/java/org/opensearch/client/Requests.java @@ -32,6 +32,7 @@ package org.opensearch.client; +import org.opensearch.action.admin.cluster.decommission.awareness.put.PutDecommissionRequest; import org.opensearch.action.admin.cluster.health.ClusterHealthRequest; import org.opensearch.action.admin.cluster.node.info.NodesInfoRequest; import org.opensearch.action.admin.cluster.node.stats.NodesStatsRequest; @@ -548,4 +549,11 @@ public static DeleteSnapshotRequest deleteSnapshotRequest(String repository, Str public static SnapshotsStatusRequest snapshotsStatusRequest(String repository) { return new SnapshotsStatusRequest(repository); } + + /** + * Creates a new decommission request. + */ + public static PutDecommissionRequest putDecommissionRequest() { + return new PutDecommissionRequest(); + } } diff --git a/server/src/main/java/org/opensearch/client/support/AbstractClient.java b/server/src/main/java/org/opensearch/client/support/AbstractClient.java index bc80a2ba92bf8..26073d84c92f1 100644 --- a/server/src/main/java/org/opensearch/client/support/AbstractClient.java +++ b/server/src/main/java/org/opensearch/client/support/AbstractClient.java @@ -43,6 +43,10 @@ import org.opensearch.action.admin.cluster.allocation.ClusterAllocationExplainRequest; import org.opensearch.action.admin.cluster.allocation.ClusterAllocationExplainRequestBuilder; import org.opensearch.action.admin.cluster.allocation.ClusterAllocationExplainResponse; +import org.opensearch.action.admin.cluster.decommission.awareness.put.PutDecommissionAction; +import org.opensearch.action.admin.cluster.decommission.awareness.put.PutDecommissionRequest; +import org.opensearch.action.admin.cluster.decommission.awareness.put.PutDecommissionRequestBuilder; +import org.opensearch.action.admin.cluster.decommission.awareness.put.PutDecommissionResponse; import org.opensearch.action.admin.cluster.health.ClusterHealthAction; import org.opensearch.action.admin.cluster.health.ClusterHealthRequest; import org.opensearch.action.admin.cluster.health.ClusterHealthRequestBuilder; @@ -1314,6 +1318,21 @@ public DeleteStoredScriptRequestBuilder prepareDeleteStoredScript() { public DeleteStoredScriptRequestBuilder prepareDeleteStoredScript(String id) { return prepareDeleteStoredScript().setId(id); } + + @Override + public ActionFuture putDecommission(PutDecommissionRequest request) { + return execute(PutDecommissionAction.INSTANCE, request); + } + + @Override + public void putDecommission(PutDecommissionRequest request, ActionListener listener) { + execute(PutDecommissionAction.INSTANCE, request, listener); + } + + @Override + public PutDecommissionRequestBuilder preparePutDecommission(PutDecommissionRequest request) { + return new PutDecommissionRequestBuilder(this, PutDecommissionAction.INSTANCE, request); + } } static class IndicesAdmin implements IndicesAdminClient { diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java new file mode 100644 index 0000000000000..15c17ae4b7ae1 --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java @@ -0,0 +1,111 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.decommission; + +import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.common.io.stream.Writeable; + +import java.io.IOException; +import java.util.Objects; + +public final class DecommissionAttribute implements Writeable { + private final String attributeName; + private final String attributeValue; + + /** + * Update the attribute value for a given attribute name to decommission + * + * @param decommissionAttribute current decommissioned attribute object + * @param attributeValue attribute value to be updated with + */ + public DecommissionAttribute(DecommissionAttribute decommissionAttribute, String attributeValue) { + this(decommissionAttribute.attributeName, attributeValue); + } + + /** + * Constructs new decommission attribute name value pair + * + * @param attributeName attribute name + * @param attributeValue attribute value + */ + public DecommissionAttribute(String attributeName, String attributeValue) { + this.attributeName = attributeName; + this.attributeValue = attributeValue; + } + + /** + * Returns attribute name + * + * @return attributeName + */ + public String attributeName() { + return this.attributeName; + } + + /** + * Returns attribute value + * + * @return attributeValue + */ + public String attributeValue() { + return this.attributeValue; + } + + public DecommissionAttribute(StreamInput in) throws IOException { + attributeName = in.readString(); + attributeValue = in.readString(); + } + + /** + * Writes decommission attribute name value to stream output + * + * @param out stream output + */ + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeString(attributeName); + out.writeString(attributeValue); + } + + /** + * Checks if this instance is equal to the other instance in attributeName but differ in attribute value {@link #attributeValue}. + * + * @param other other decommission attribute name value + * @return {@code true} if both instances equal in attributeName fields but the attributeValue field + */ + public boolean equalsIgnoreValues(DecommissionAttribute other) { + return attributeName.equals(other.attributeName); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + DecommissionAttribute that = (DecommissionAttribute) o; + + if (!attributeName.equals(that.attributeName)) return false; + return attributeValue.equals(that.attributeValue); + } + + @Override + public int hashCode() { + return Objects.hash(attributeName, attributeValue); + } + + + @Override + public String toString() { + return "DecommissionAttribute{" + + "attributeName='" + attributeName + '\'' + + ", attributeValue='" + attributeValue + '\'' + + '}'; + } +} diff --git a/server/src/main/java/org/opensearch/rest/action/admin/cluster/RestPutDecommissionAction.java b/server/src/main/java/org/opensearch/rest/action/admin/cluster/RestPutDecommissionAction.java new file mode 100644 index 0000000000000..9ad6d04703e60 --- /dev/null +++ b/server/src/main/java/org/opensearch/rest/action/admin/cluster/RestPutDecommissionAction.java @@ -0,0 +1,65 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.rest.action.admin.cluster; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.action.admin.cluster.decommission.awareness.put.PutDecommissionRequest; +import org.opensearch.client.Requests; +import org.opensearch.client.node.NodeClient; +import org.opensearch.cluster.decommission.DecommissionAttribute; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.rest.BaseRestHandler; +import org.opensearch.rest.RestRequest; +import org.opensearch.rest.action.RestToXContentListener; + +import java.io.IOException; +import java.util.List; + +import static java.util.Collections.singletonList; +import static org.opensearch.rest.RestRequest.Method.PUT; + +public class RestPutDecommissionAction extends BaseRestHandler { + + private static final TimeValue DEFAULT_TIMEOUT = TimeValue.timeValueSeconds(300L); + + @Override + public List routes() { + return singletonList(new Route(PUT, "/_cluster/decommission/awareness/{awareness_attribute_name}/{awareness_attribute_value}")); + } + + @Override + public String getName() { + return "put_decommission_action"; + } + + @Override + protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException { + PutDecommissionRequest putDecommissionRequest = createRequest(request); + return channel -> client.admin() + .cluster() + .putDecommission(putDecommissionRequest, new RestToXContentListener<>(channel)); + } + + PutDecommissionRequest createRequest(RestRequest request) throws IOException { + String attributeName = null; + String attributeValue = null; + PutDecommissionRequest putDecommissionRequest = Requests.putDecommissionRequest(); + if (request.hasParam("awareness_attribute_name")) { + attributeName = request.param("awareness_attribute_name"); + } + + if (request.hasParam("awareness_attribute_value")) { + attributeValue = request.param("awareness_attribute_value"); + } + return putDecommissionRequest + .setDecommissionAttribute(new DecommissionAttribute(attributeName, attributeValue)) + .setTimeout(TimeValue.parseTimeValue(request.param("timeout"), DEFAULT_TIMEOUT, getClass().getSimpleName() + ".timeout")); + } +} From a97a6f33ae85acb26ba967ae2eb4542c5c3c8803 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Thu, 18 Aug 2022 17:43:26 +0530 Subject: [PATCH 075/187] Add Get Decommission API Signed-off-by: Rishab Nahata --- .../org/opensearch/action/ActionModule.java | 5 + .../awareness/get/GetDecommissionAction.java | 21 ++++ .../awareness/get/GetDecommissionRequest.java | 41 +++++++ .../get/GetDecommissionRequestBuilder.java | 27 +++++ .../get/GetDecommissionResponse.java | 104 ++++++++++++++++++ .../get/TransportGetDecommissionAction.java | 83 ++++++++++++++ .../opensearch/client/ClusterAdminClient.java | 18 +++ .../java/org/opensearch/client/Requests.java | 12 ++ .../client/support/AbstractClient.java | 20 ++++ .../decommission/DecommissionStatus.java | 93 ++++++++++++++++ .../cluster/RestGetDecommissionAction.java | 55 +++++++++ 11 files changed, 479 insertions(+) create mode 100644 server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionAction.java create mode 100644 server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionRequest.java create mode 100644 server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionRequestBuilder.java create mode 100644 server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponse.java create mode 100644 server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/TransportGetDecommissionAction.java create mode 100644 server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java create mode 100644 server/src/main/java/org/opensearch/rest/action/admin/cluster/RestGetDecommissionAction.java diff --git a/server/src/main/java/org/opensearch/action/ActionModule.java b/server/src/main/java/org/opensearch/action/ActionModule.java index 5f53b0f1074bf..900d50a155c92 100644 --- a/server/src/main/java/org/opensearch/action/ActionModule.java +++ b/server/src/main/java/org/opensearch/action/ActionModule.java @@ -40,6 +40,8 @@ import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsAction; import org.opensearch.action.admin.cluster.configuration.TransportAddVotingConfigExclusionsAction; import org.opensearch.action.admin.cluster.configuration.TransportClearVotingConfigExclusionsAction; +import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionAction; +import org.opensearch.action.admin.cluster.decommission.awareness.get.TransportGetDecommissionAction; import org.opensearch.action.admin.cluster.decommission.awareness.put.PutDecommissionAction; import org.opensearch.action.admin.cluster.decommission.awareness.put.TransportPutDecommissionAction; import org.opensearch.action.admin.cluster.health.ClusterHealthAction; @@ -307,6 +309,7 @@ import org.opensearch.rest.action.admin.cluster.RestDeleteRepositoryAction; import org.opensearch.rest.action.admin.cluster.RestDeleteSnapshotAction; import org.opensearch.rest.action.admin.cluster.RestDeleteStoredScriptAction; +import org.opensearch.rest.action.admin.cluster.RestGetDecommissionAction; import org.opensearch.rest.action.admin.cluster.RestGetRepositoriesAction; import org.opensearch.rest.action.admin.cluster.RestGetScriptContextAction; import org.opensearch.rest.action.admin.cluster.RestGetScriptLanguageAction; @@ -688,6 +691,7 @@ public void reg // Decommission actions actions.register(PutDecommissionAction.INSTANCE, TransportPutDecommissionAction.class); + actions.register(GetDecommissionAction.INSTANCE, TransportGetDecommissionAction.class); return unmodifiableMap(actions.getRegistry()); } @@ -880,6 +884,7 @@ public void initRestHandlers(Supplier nodesInCluster) { } registerHandler.accept(new RestCatAction(catActions)); registerHandler.accept(new RestPutDecommissionAction()); + registerHandler.accept(new RestGetDecommissionAction()); // Remote Store APIs if (FeatureFlags.isEnabled(FeatureFlags.REMOTE_STORE)) { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionAction.java new file mode 100644 index 0000000000000..21e75c1dfee3e --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionAction.java @@ -0,0 +1,21 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.decommission.awareness.get; + +import org.opensearch.action.ActionType; + +public class GetDecommissionAction extends ActionType { + + public static final GetDecommissionAction INSTANCE = new GetDecommissionAction(); + public static final String NAME = "cluster:admin/decommission/awareness/get"; + + private GetDecommissionAction() { + super(NAME, GetDecommissionResponse::new); + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionRequest.java new file mode 100644 index 0000000000000..63114cfa8ec25 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionRequest.java @@ -0,0 +1,41 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.decommission.awareness.get; + +import org.opensearch.action.ActionRequestValidationException; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadRequest; +import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.common.io.stream.StreamOutput; + +import java.io.IOException; + +/** + * Get Decommissioned attribute request + * + * @opensearch.internal + */ +public class GetDecommissionRequest extends ClusterManagerNodeReadRequest { + + public GetDecommissionRequest() { + } + + public GetDecommissionRequest(StreamInput in) throws IOException { + super(in); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + } + + @Override + public ActionRequestValidationException validate() { + return null; + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionRequestBuilder.java new file mode 100644 index 0000000000000..6a1d93cfebe34 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionRequestBuilder.java @@ -0,0 +1,27 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.decommission.awareness.get; + +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadOperationRequestBuilder; +import org.opensearch.client.OpenSearchClient; + +public class GetDecommissionRequestBuilder extends ClusterManagerNodeReadOperationRequestBuilder< + GetDecommissionRequest, + GetDecommissionResponse, + GetDecommissionRequestBuilder> { + + // TODO - Support for get attribute with the name? + + /** + * Creates new get decommissioned attributes request builder + */ + public GetDecommissionRequestBuilder(OpenSearchClient client, GetDecommissionAction action) { + super(client, action, new GetDecommissionRequest()); + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponse.java new file mode 100644 index 0000000000000..484ce261e502f --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponse.java @@ -0,0 +1,104 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.decommission.awareness.get; + +import org.opensearch.OpenSearchParseException; +import org.opensearch.action.ActionResponse; +import org.opensearch.cluster.decommission.DecommissionAttribute; +import org.opensearch.cluster.decommission.DecommissionStatus; +import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.common.xcontent.ToXContentObject; +import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.xcontent.XContentParser; + +import java.io.IOException; + +import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken; + +public class GetDecommissionResponse extends ActionResponse implements ToXContentObject { + + private final DecommissionStatus status; + private final DecommissionAttribute decommissionedAttribute; + + + GetDecommissionResponse(DecommissionAttribute decommissionedAttribute, DecommissionStatus status) { + this.decommissionedAttribute = decommissionedAttribute; + this.status = status; + } + + GetDecommissionResponse(StreamInput in) throws IOException { + this.decommissionedAttribute = new DecommissionAttribute(in); + this.status = DecommissionStatus.fromValue(in.readByte()); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + decommissionedAttribute.writeTo(out); + out.writeByte(status.value()); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + builder.startObject("awareness"); + builder.field(decommissionedAttribute.attributeName(), decommissionedAttribute.attributeValue()); + builder.endObject(); + builder.field("status", status); + builder.endObject(); + return builder; + } + + public static GetDecommissionResponse fromXContent(XContentParser parser) throws IOException { + ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser); + String attributeType = "awareness"; + XContentParser.Token token; + DecommissionAttribute decommissionAttribute = null; + DecommissionStatus status = null; + if ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + String currentFieldName = parser.currentName(); + if (attributeType.equals(currentFieldName)) { + if (parser.nextToken() != XContentParser.Token.START_OBJECT) { + throw new OpenSearchParseException("failed to parse decommission attribute type [{}], expected object", attributeType); + } + token = parser.nextToken(); + if (token != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + String fieldName = parser.currentName(); + String value; + token = parser.nextToken(); + if (token == XContentParser.Token.VALUE_STRING) { + value = parser.text(); + } else { + throw new OpenSearchParseException("failed to parse attribute [{}], expected string for attribute value", fieldName); + } + decommissionAttribute = new DecommissionAttribute(fieldName, value); + } else { + throw new OpenSearchParseException("failed to parse attribute type [{}], unexpected type", attributeType); + } + } else { + throw new OpenSearchParseException("failed to parse attribute type [{}]", attributeType); + } + } else if ("status".equals(currentFieldName)) { + if (parser.nextToken() != XContentParser.Token.VALUE_STRING) { + throw new OpenSearchParseException("failed to parse status of decommissioning, expected string but found unknown type"); + } + status = DecommissionStatus.fromString(parser.text()); + } else { + throw new OpenSearchParseException( + "unknown field found [{}], failed to parse the decommission attribute", + currentFieldName + ); + } + } + } + return new GetDecommissionResponse(decommissionAttribute, status); + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/TransportGetDecommissionAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/TransportGetDecommissionAction.java new file mode 100644 index 0000000000000..6627b78d664f0 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/TransportGetDecommissionAction.java @@ -0,0 +1,83 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.decommission.awareness.get; + +import org.opensearch.action.ActionListener; +import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeReadAction; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.block.ClusterBlockException; +import org.opensearch.cluster.block.ClusterBlockLevel; +import org.opensearch.cluster.decommission.DecommissionAttribute; +import org.opensearch.cluster.decommission.DecommissionStatus; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.metadata.Metadata; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.inject.Inject; +import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportService; + +import java.io.IOException; + +public class TransportGetDecommissionAction extends TransportClusterManagerNodeReadAction< + GetDecommissionRequest, + GetDecommissionResponse> { + + @Inject + public TransportGetDecommissionAction( + TransportService transportService, + ClusterService clusterService, + ThreadPool threadPool, + ActionFilters actionFilters, + IndexNameExpressionResolver indexNameExpressionResolver + ) { + super( + GetDecommissionAction.NAME, + transportService, + clusterService, + threadPool, + actionFilters, + GetDecommissionRequest::new, + indexNameExpressionResolver + ); + } + + @Override + protected String executor() { + return ThreadPool.Names.SAME; + } + + @Override + protected GetDecommissionResponse read(StreamInput in) throws IOException { + return new GetDecommissionResponse(in); + } + + @Override + protected void clusterManagerOperation( + GetDecommissionRequest request, + ClusterState state, + ActionListener listener + ) throws Exception { + Metadata metadata = state.metadata(); +// DecommissionAttributeMetadata decommissionedAttributes = metadata.custom(DecommissionAttributeMetadata.TYPE); + // TODO - update once service layer changes are merged + listener.onResponse( + new GetDecommissionResponse( + new DecommissionAttribute("zone", "zone-1"), + DecommissionStatus.DECOMMISSIONED + ) + ); + } + + @Override + protected ClusterBlockException checkBlock(GetDecommissionRequest request, ClusterState state) { + return state.blocks().globalBlockedException(ClusterBlockLevel.METADATA_READ); + } +} diff --git a/server/src/main/java/org/opensearch/client/ClusterAdminClient.java b/server/src/main/java/org/opensearch/client/ClusterAdminClient.java index b8536f0fabbaf..0c5a7c2b30809 100644 --- a/server/src/main/java/org/opensearch/client/ClusterAdminClient.java +++ b/server/src/main/java/org/opensearch/client/ClusterAdminClient.java @@ -37,6 +37,9 @@ import org.opensearch.action.admin.cluster.allocation.ClusterAllocationExplainRequest; import org.opensearch.action.admin.cluster.allocation.ClusterAllocationExplainRequestBuilder; import org.opensearch.action.admin.cluster.allocation.ClusterAllocationExplainResponse; +import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionRequest; +import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionRequestBuilder; +import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionResponse; import org.opensearch.action.admin.cluster.decommission.awareness.put.PutDecommissionRequest; import org.opensearch.action.admin.cluster.decommission.awareness.put.PutDecommissionRequestBuilder; import org.opensearch.action.admin.cluster.decommission.awareness.put.PutDecommissionResponse; @@ -809,4 +812,19 @@ public interface ClusterAdminClient extends OpenSearchClient { * Decommission a node */ PutDecommissionRequestBuilder preparePutDecommission(PutDecommissionRequest request); + + /** + * Get Decommissioned attribute + */ + ActionFuture getDecommission(GetDecommissionRequest request); + + /** + * Get Decommissioned attribute + */ + void getDecommission(GetDecommissionRequest request, ActionListener listener); + + /** + * Get Decommissioned attribute + */ + GetDecommissionRequestBuilder prepareGetDecommission(); } diff --git a/server/src/main/java/org/opensearch/client/Requests.java b/server/src/main/java/org/opensearch/client/Requests.java index 44b8b9e210bf7..ee97d0412a7c9 100644 --- a/server/src/main/java/org/opensearch/client/Requests.java +++ b/server/src/main/java/org/opensearch/client/Requests.java @@ -32,6 +32,7 @@ package org.opensearch.client; +import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionRequest; import org.opensearch.action.admin.cluster.decommission.awareness.put.PutDecommissionRequest; import org.opensearch.action.admin.cluster.health.ClusterHealthRequest; import org.opensearch.action.admin.cluster.node.info.NodesInfoRequest; @@ -552,8 +553,19 @@ public static SnapshotsStatusRequest snapshotsStatusRequest(String repository) { /** * Creates a new decommission request. + * + * @return returns put decommission request */ public static PutDecommissionRequest putDecommissionRequest() { return new PutDecommissionRequest(); } + + /** + * Get decommissioned attribute from metadata + * + * @return returns get decommission request + */ + public static GetDecommissionRequest getDecommissionRequest() { + return new GetDecommissionRequest(); + } } diff --git a/server/src/main/java/org/opensearch/client/support/AbstractClient.java b/server/src/main/java/org/opensearch/client/support/AbstractClient.java index 26073d84c92f1..c120b24b1efd9 100644 --- a/server/src/main/java/org/opensearch/client/support/AbstractClient.java +++ b/server/src/main/java/org/opensearch/client/support/AbstractClient.java @@ -43,6 +43,10 @@ import org.opensearch.action.admin.cluster.allocation.ClusterAllocationExplainRequest; import org.opensearch.action.admin.cluster.allocation.ClusterAllocationExplainRequestBuilder; import org.opensearch.action.admin.cluster.allocation.ClusterAllocationExplainResponse; +import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionAction; +import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionRequest; +import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionRequestBuilder; +import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionResponse; import org.opensearch.action.admin.cluster.decommission.awareness.put.PutDecommissionAction; import org.opensearch.action.admin.cluster.decommission.awareness.put.PutDecommissionRequest; import org.opensearch.action.admin.cluster.decommission.awareness.put.PutDecommissionRequestBuilder; @@ -1333,6 +1337,22 @@ public void putDecommission(PutDecommissionRequest request, ActionListener getDecommission(GetDecommissionRequest request) { + return execute(GetDecommissionAction.INSTANCE, request); + } + + @Override + public void getDecommission(GetDecommissionRequest request, ActionListener listener) { + execute(GetDecommissionAction.INSTANCE, request, listener); + } + + @Override + public GetDecommissionRequestBuilder prepareGetDecommission() { + return new GetDecommissionRequestBuilder(this, GetDecommissionAction.INSTANCE); + } + } static class IndicesAdmin implements IndicesAdminClient { diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java new file mode 100644 index 0000000000000..ccf318fb94bcb --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java @@ -0,0 +1,93 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.decommission; + +public enum DecommissionStatus { + /** + * Decommission process is initiated + */ + INIT((byte) 0), + /** + * Decommission process has started, decommissioned nodes should be weighed away + */ + DECOMMISSIONING((byte) 1), + /** + * Decommissioning awareness attribute completed + */ + DECOMMISSIONED((byte) 2), + /** + * Decommission request failed + */ + DECOMMISSION_FAILED((byte) 3), + /** + * Recommission request received, recommissioning process has started + */ + RECOMMISSIONING((byte) 4), + /** + * Recommission request failed. No nodes should fail to join the cluster with decommission exception + */ + RECOMMISSION_FAILED((byte) 5); + + private final byte value; + + DecommissionStatus(byte value) { + this.value = value; + } + + /** + * Returns code that represents the decommission state + * + * @return code for the state + */ + public byte value() { + return value; + } + + /** + * Generate decommission state from code + * + * @param value the state code + * @return state + */ + public static DecommissionStatus fromValue(byte value) { + switch (value) { + case 0: + return INIT; + case 1: + return DECOMMISSIONING; + case 2: + return DECOMMISSIONED; + case 3: + return DECOMMISSION_FAILED; + case 4: + return RECOMMISSIONING; + case 5: + return RECOMMISSION_FAILED; + default: + throw new IllegalArgumentException("No decommission state for value [" + value + "]"); + } + } + + public static DecommissionStatus fromString(String status) { + if ("init".equals(status)) { + return INIT; + } else if ("decommissioning".equals(status)) { + return DECOMMISSIONING; + } else if ("decommissioned".equals(status)) { + return DECOMMISSIONED; + } else if ("decommission_failed".equals(status)) { + return DECOMMISSION_FAILED; + } else if ("recommissioning".equals(status)) { + return RECOMMISSIONING; + } else if ("recommission_failed".equals(status)) { + return RECOMMISSION_FAILED; + } + throw new IllegalStateException("No status match for [" + status + "]"); + } +} diff --git a/server/src/main/java/org/opensearch/rest/action/admin/cluster/RestGetDecommissionAction.java b/server/src/main/java/org/opensearch/rest/action/admin/cluster/RestGetDecommissionAction.java new file mode 100644 index 0000000000000..d9d0631c90268 --- /dev/null +++ b/server/src/main/java/org/opensearch/rest/action/admin/cluster/RestGetDecommissionAction.java @@ -0,0 +1,55 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.rest.action.admin.cluster; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionRequest; +import org.opensearch.client.Requests; +import org.opensearch.client.node.NodeClient; +import org.opensearch.rest.BaseRestHandler; +import org.opensearch.rest.RestRequest; +import org.opensearch.rest.action.RestToXContentListener; + +import java.io.IOException; +import java.util.List; + +import static java.util.Collections.singletonList; +import static org.opensearch.rest.RestRequest.Method.GET; + +/** + * Returns decommissioned attribute information + * + * @opensearch.api + */ +public class RestGetDecommissionAction extends BaseRestHandler { + + private static final Logger logger = LogManager.getLogger(RestPutDecommissionAction.class); + + @Override + public List routes() { + return singletonList(new Route(GET, "/_cluster/decommission/awareness/_status")); + } + + @Override + public String getName() { + return "get_decommission_action"; + } + + @Override + public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException { + GetDecommissionRequest getDecommissionRequest = Requests.getDecommissionRequest(); + getDecommissionRequest.clusterManagerNodeTimeout( + request.paramAsTime("cluster_manager_timeout", getDecommissionRequest.clusterManagerNodeTimeout()) + ); + return channel -> client.admin() + .cluster() + .getDecommission(getDecommissionRequest, new RestToXContentListener<>(channel)); + } +} From 6560b84184d602a4acd3a181795c93c357df5239 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Mon, 22 Aug 2022 12:27:41 +0530 Subject: [PATCH 076/187] Adding UT Signed-off-by: Rishab Nahata --- .../put/PutDecommissionRequestTests.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequestTests.java diff --git a/server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequestTests.java b/server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequestTests.java new file mode 100644 index 0000000000000..6ef6eff44b136 --- /dev/null +++ b/server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequestTests.java @@ -0,0 +1,39 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.decommission.awareness.put; + +import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsRequest; +import org.opensearch.cluster.decommission.DecommissionAttribute; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.test.OpenSearchTestCase; + +import java.io.IOException; + +public class PutDecommissionRequestTests extends OpenSearchTestCase { + + public void testSerialization() throws IOException { + String attributeName = "zone"; + String attributeValue = "zone-1"; + DecommissionAttribute decommissionAttribute = new DecommissionAttribute(attributeName, attributeValue); + TimeValue timeout = TimeValue.timeValueMillis(between(0, 30000)); + final PutDecommissionRequest originalRequest = new PutDecommissionRequest( + decommissionAttribute, + timeout + ); + + final PutDecommissionRequest deserialized = copyWriteable( + originalRequest, + writableRegistry(), + PutDecommissionRequest::new + ); + + assertEquals(deserialized.getDecommissionAttribute(), originalRequest.getDecommissionAttribute()); + assertEquals(deserialized.getTimeout(), originalRequest.getTimeout()); + } +} From 9d9f931f00ed7e3b223541ad41cd35e38ace1bb0 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Mon, 22 Aug 2022 16:15:56 +0530 Subject: [PATCH 077/187] Add UTs for Rest Layer Signed-off-by: Rishab Nahata --- ...cluster.delete_decommission_awareness.json | 35 ---------- .../get/GetDecommissionRequestBuilder.java | 2 - .../get/GetDecommissionResponse.java | 28 +++++++- .../awareness/put/PutDecommissionRequest.java | 8 +-- .../put/PutDecommissionResponse.java | 1 + .../decommission/DecommissionStatus.java | 12 ++-- .../cluster/RestGetDecommissionAction.java | 2 - .../get/GetDecommissionResponseTests.java | 37 +++++++++++ .../put/PutDecommissionRequestTests.java | 46 ++++++++++++- .../put/PutDecommissionResponseTests.java | 21 ++++++ .../RestPutDecommissionActionTests.java | 65 +++++++++++++++++++ 11 files changed, 202 insertions(+), 55 deletions(-) delete mode 100644 rest-api-spec/src/main/resources/rest-api-spec/api/cluster.delete_decommission_awareness.json create mode 100644 server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponseTests.java create mode 100644 server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionResponseTests.java create mode 100644 server/src/test/java/org/opensearch/rest/action/admin/cluster/RestPutDecommissionActionTests.java diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.delete_decommission_awareness.json b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.delete_decommission_awareness.json deleted file mode 100644 index a28eba8429fd0..0000000000000 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.delete_decommission_awareness.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "cluster.delete_decommission_awareness": { - "documentation": { - "url": "TBA", - "description": "TBA" - }, - "stability": "experimental", - "url": { - "paths": [ - { - "path": "/_cluster/decommission/awareness/{awareness_attribute_name}/{awareness_attribute_value}", - "methods": [ - "DELETE" - ], - "parts": { - "awareness_attribute_name": { - "type": "string", - "description": "Awareness attribute name" - }, - "awareness_attribute_value": { - "type": "string", - "description": "Awareness attribute value" - } - } - } - ] - }, - "params": { - "timeout": { - "type": "time", - "description": "Explicit operation timeout" - } - } - } -} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionRequestBuilder.java index 6a1d93cfebe34..08a702cdc7524 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionRequestBuilder.java @@ -16,8 +16,6 @@ public class GetDecommissionRequestBuilder extends ClusterManagerNodeReadOperati GetDecommissionResponse, GetDecommissionRequestBuilder> { - // TODO - Support for get attribute with the name? - /** * Creates new get decommissioned attributes request builder */ diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponse.java index 484ce261e502f..1b3aa32c18393 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponse.java @@ -19,14 +19,14 @@ import org.opensearch.common.xcontent.XContentParser; import java.io.IOException; +import java.util.Objects; import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken; public class GetDecommissionResponse extends ActionResponse implements ToXContentObject { - private final DecommissionStatus status; private final DecommissionAttribute decommissionedAttribute; - + private final DecommissionStatus status; GetDecommissionResponse(DecommissionAttribute decommissionedAttribute, DecommissionStatus status) { this.decommissionedAttribute = decommissionedAttribute; @@ -44,6 +44,14 @@ public void writeTo(StreamOutput out) throws IOException { out.writeByte(status.value()); } + public DecommissionAttribute getDecommissionedAttribute() { + return decommissionedAttribute; + } + + public DecommissionStatus getDecommissionStatus() { + return status; + } + @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(); @@ -61,7 +69,7 @@ public static GetDecommissionResponse fromXContent(XContentParser parser) throws XContentParser.Token token; DecommissionAttribute decommissionAttribute = null; DecommissionStatus status = null; - if ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) { String currentFieldName = parser.currentName(); if (attributeType.equals(currentFieldName)) { @@ -80,6 +88,7 @@ public static GetDecommissionResponse fromXContent(XContentParser parser) throws throw new OpenSearchParseException("failed to parse attribute [{}], expected string for attribute value", fieldName); } decommissionAttribute = new DecommissionAttribute(fieldName, value); + token = parser.nextToken(); } else { throw new OpenSearchParseException("failed to parse attribute type [{}], unexpected type", attributeType); } @@ -101,4 +110,17 @@ public static GetDecommissionResponse fromXContent(XContentParser parser) throws } return new GetDecommissionResponse(decommissionAttribute, status); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + GetDecommissionResponse that = (GetDecommissionResponse) o; + return decommissionedAttribute.equals(that.decommissionedAttribute) && status == that.status; + } + + @Override + public int hashCode() { + return Objects.hash(decommissionedAttribute, status); + } } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequest.java index fcbc77727b5c8..fd78deb86614b 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequest.java @@ -32,10 +32,6 @@ public PutDecommissionRequest(DecommissionAttribute decommissionAttribute, TimeV this.timeout = timeout; } - public PutDecommissionRequest(DecommissionAttribute decommissionAttribute) { - this.decommissionAttribute = decommissionAttribute; - } - public PutDecommissionRequest(StreamInput in) throws IOException { super(in); decommissionAttribute = new DecommissionAttribute(in); @@ -85,10 +81,10 @@ public TimeValue getTimeout() { @Override public ActionRequestValidationException validate() { ActionRequestValidationException validationException = null; - if (decommissionAttribute.attributeName() == null) { + if (decommissionAttribute.attributeName() == null || decommissionAttribute.attributeName().isEmpty()) { validationException = addValidationError("attribute name is missing", validationException); } - if (decommissionAttribute.attributeValue() == null) { + if (decommissionAttribute.attributeValue() == null || decommissionAttribute.attributeValue().isEmpty()) { validationException = addValidationError("attribute value is missing", validationException); } return validationException; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionResponse.java index 695e894aaa886..c2fdbc48ab286 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionResponse.java @@ -12,6 +12,7 @@ import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.xcontent.ToXContentObject; +import org.opensearch.common.xcontent.XContentBuilder; import java.io.IOException; diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java index ccf318fb94bcb..208bddac3cf29 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java @@ -75,17 +75,17 @@ public static DecommissionStatus fromValue(byte value) { } public static DecommissionStatus fromString(String status) { - if ("init".equals(status)) { + if ("INIT".equals(status)) { return INIT; - } else if ("decommissioning".equals(status)) { + } else if ("DECOMMISSIONING".equals(status)) { return DECOMMISSIONING; - } else if ("decommissioned".equals(status)) { + } else if ("DECOMMISSIONED".equals(status)) { return DECOMMISSIONED; - } else if ("decommission_failed".equals(status)) { + } else if ("DECOMMISSION_FAILED".equals(status)) { return DECOMMISSION_FAILED; - } else if ("recommissioning".equals(status)) { + } else if ("RECOMMISSIONING".equals(status)) { return RECOMMISSIONING; - } else if ("recommission_failed".equals(status)) { + } else if ("RECOMMISSION_FAILED".equals(status)) { return RECOMMISSION_FAILED; } throw new IllegalStateException("No status match for [" + status + "]"); diff --git a/server/src/main/java/org/opensearch/rest/action/admin/cluster/RestGetDecommissionAction.java b/server/src/main/java/org/opensearch/rest/action/admin/cluster/RestGetDecommissionAction.java index d9d0631c90268..8381317253a17 100644 --- a/server/src/main/java/org/opensearch/rest/action/admin/cluster/RestGetDecommissionAction.java +++ b/server/src/main/java/org/opensearch/rest/action/admin/cluster/RestGetDecommissionAction.java @@ -30,8 +30,6 @@ */ public class RestGetDecommissionAction extends BaseRestHandler { - private static final Logger logger = LogManager.getLogger(RestPutDecommissionAction.class); - @Override public List routes() { return singletonList(new Route(GET, "/_cluster/decommission/awareness/_status")); diff --git a/server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponseTests.java b/server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponseTests.java new file mode 100644 index 0000000000000..be6d336128882 --- /dev/null +++ b/server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponseTests.java @@ -0,0 +1,37 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.decommission.awareness.get; + +import org.opensearch.cluster.decommission.DecommissionAttribute; +import org.opensearch.cluster.decommission.DecommissionStatus; +import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.test.AbstractXContentTestCase; + +import java.io.IOException; + +public class GetDecommissionResponseTests extends AbstractXContentTestCase { + @Override + protected GetDecommissionResponse createTestInstance() { + DecommissionStatus status = DecommissionStatus.fromValue((byte) randomIntBetween(0, 5)); + String attributeName = randomAlphaOfLength(10); + String attributeValue = randomAlphaOfLength(10); + DecommissionAttribute decommissionAttribute = new DecommissionAttribute(attributeName, attributeValue); + return new GetDecommissionResponse(decommissionAttribute, status); + } + + @Override + protected GetDecommissionResponse doParseInstance(XContentParser parser) throws IOException { + return GetDecommissionResponse.fromXContent(parser); + } + + @Override + protected boolean supportsUnknownFields() { + return false; + } +} diff --git a/server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequestTests.java b/server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequestTests.java index 6ef6eff44b136..7f1be10232f3e 100644 --- a/server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequestTests.java +++ b/server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequestTests.java @@ -8,7 +8,7 @@ package org.opensearch.action.admin.cluster.decommission.awareness.put; -import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsRequest; +import org.opensearch.action.ActionRequestValidationException; import org.opensearch.cluster.decommission.DecommissionAttribute; import org.opensearch.common.unit.TimeValue; import org.opensearch.test.OpenSearchTestCase; @@ -36,4 +36,48 @@ public void testSerialization() throws IOException { assertEquals(deserialized.getDecommissionAttribute(), originalRequest.getDecommissionAttribute()); assertEquals(deserialized.getTimeout(), originalRequest.getTimeout()); } + + public void testValidation() { + { + String attributeName = null; + String attributeValue = "test"; + DecommissionAttribute decommissionAttribute = new DecommissionAttribute(attributeName, attributeValue); + TimeValue timeout = TimeValue.timeValueMillis(between(0, 30000)); + + final PutDecommissionRequest request = new PutDecommissionRequest( + decommissionAttribute, + timeout + ); + ActionRequestValidationException e = request.validate(); + assertNotNull(e); + assertTrue(e.getMessage().contains("attribute name is missing")); + } + { + String attributeName = "zone"; + String attributeValue = ""; + DecommissionAttribute decommissionAttribute = new DecommissionAttribute(attributeName, attributeValue); + TimeValue timeout = TimeValue.timeValueMillis(between(0, 30000)); + + final PutDecommissionRequest request = new PutDecommissionRequest( + decommissionAttribute, + timeout + ); + ActionRequestValidationException e = request.validate(); + assertNotNull(e); + assertTrue(e.getMessage().contains("attribute value is missing")); + } + { + String attributeName = "zone"; + String attributeValue = "test"; + DecommissionAttribute decommissionAttribute = new DecommissionAttribute(attributeName, attributeValue); + TimeValue timeout = TimeValue.timeValueMillis(between(0, 30000)); + + final PutDecommissionRequest request = new PutDecommissionRequest( + decommissionAttribute, + timeout + ); + ActionRequestValidationException e = request.validate(); + assertNull(e); + } + } } diff --git a/server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionResponseTests.java b/server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionResponseTests.java new file mode 100644 index 0000000000000..91f1cf3d9eb66 --- /dev/null +++ b/server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionResponseTests.java @@ -0,0 +1,21 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.decommission.awareness.put; + +import org.opensearch.test.OpenSearchTestCase; + +import java.io.IOException; + +public class PutDecommissionResponseTests extends OpenSearchTestCase { + public void testSerialization() throws IOException { + final PutDecommissionResponse originalRequest = new PutDecommissionResponse(true); + copyWriteable(originalRequest, writableRegistry(), PutDecommissionResponse::new); + // there are no fields so we're just checking that this doesn't throw anything + } +} diff --git a/server/src/test/java/org/opensearch/rest/action/admin/cluster/RestPutDecommissionActionTests.java b/server/src/test/java/org/opensearch/rest/action/admin/cluster/RestPutDecommissionActionTests.java new file mode 100644 index 0000000000000..f5ff48e787637 --- /dev/null +++ b/server/src/test/java/org/opensearch/rest/action/admin/cluster/RestPutDecommissionActionTests.java @@ -0,0 +1,65 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.rest.action.admin.cluster; + +import org.junit.Before; +import org.opensearch.action.admin.cluster.decommission.awareness.put.PutDecommissionRequest; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.rest.RestRequest; +import org.opensearch.test.rest.FakeRestRequest; +import org.opensearch.test.rest.RestActionTestCase; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class RestPutDecommissionActionTests extends RestActionTestCase { + + private RestPutDecommissionAction action; + + @Before + public void setupAction() { + action = new RestPutDecommissionAction(); + controller().registerHandler(action); + } + + public void testCreateRequest() throws IOException { + Map params = new HashMap<>(); + params.put("awareness_attribute_name", "zone"); + params.put("awareness_attribute_value", "zone-1"); + params.put("timeout", "10s"); + + RestRequest deprecatedRequest = buildRestRequest(params); + + PutDecommissionRequest request = action.createRequest(deprecatedRequest); + assertEquals(request.getDecommissionAttribute().attributeName(), "zone"); + assertEquals(request.getDecommissionAttribute().attributeValue(), "zone-1"); + assertEquals(request.getTimeout(), TimeValue.timeValueSeconds(10L)); + } + + public void testCreateRequestWithDefaultTimeout() throws IOException { + Map params = new HashMap<>(); + params.put("awareness_attribute_name", "zone"); + params.put("awareness_attribute_value", "zone-1"); + + RestRequest deprecatedRequest = buildRestRequest(params); + + PutDecommissionRequest request = action.createRequest(deprecatedRequest); + assertEquals(request.getDecommissionAttribute().attributeName(), "zone"); + assertEquals(request.getDecommissionAttribute().attributeValue(), "zone-1"); + assertEquals(request.getTimeout(), TimeValue.timeValueSeconds(300L)); + } + + private FakeRestRequest buildRestRequest(Map params) { + return new FakeRestRequest.Builder(xContentRegistry()).withMethod(RestRequest.Method.GET) + .withPath("/_cluster/decommission/awareness/{awareness_attribute_name}/{awareness_attribute_value}") + .withParams(params) + .build(); + } +} From c26212fec7b1ec89648df672ef1f831c2c19d7fb Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Mon, 22 Aug 2022 16:22:53 +0530 Subject: [PATCH 078/187] Fix Signed-off-by: Rishab Nahata --- .../awareness/put/TransportPutDecommissionAction.java | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/TransportPutDecommissionAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/TransportPutDecommissionAction.java index e7a37853ef9bd..7617385dbefb4 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/TransportPutDecommissionAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/TransportPutDecommissionAction.java @@ -75,6 +75,7 @@ protected void clusterManagerOperation( ActionListener listener ) throws Exception { logger.info("initiating awareness attribute [{}] decommissioning", request.getDecommissionAttribute().toString()); + listener.onResponse(new PutDecommissionResponse(true)); // TODO - remove after integration // TODO - uncomment when integrating with the service // decommissionService.initiateAttributeDecommissioning( // request.getDecommissionAttribute(), From b781ca34017a8b0077c6900857587b8909c99224 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Mon, 22 Aug 2022 16:51:55 +0530 Subject: [PATCH 079/187] Applying spotless check Signed-off-by: Rishab Nahata --- .../awareness/get/GetDecommissionRequest.java | 3 +-- .../get/GetDecommissionResponse.java | 14 +++++++--- .../get/TransportGetDecommissionAction.java | 13 +++------- .../awareness/put/PutDecommissionRequest.java | 10 ++----- .../put/PutDecommissionRequestBuilder.java | 6 ++++- .../put/PutDecommissionResponse.java | 1 - .../put/TransportPutDecommissionAction.java | 20 +++++++------- .../decommission/DecommissionAttribute.java | 6 +---- .../cluster/RestGetDecommissionAction.java | 6 +---- .../cluster/RestPutDecommissionAction.java | 9 ++----- .../put/PutDecommissionRequestTests.java | 26 ++++--------------- 11 files changed, 40 insertions(+), 74 deletions(-) diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionRequest.java index 63114cfa8ec25..fa979544129c2 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionRequest.java @@ -22,8 +22,7 @@ */ public class GetDecommissionRequest extends ClusterManagerNodeReadRequest { - public GetDecommissionRequest() { - } + public GetDecommissionRequest() {} public GetDecommissionRequest(StreamInput in) throws IOException { super(in); diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponse.java index 1b3aa32c18393..343868894620a 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponse.java @@ -74,7 +74,10 @@ public static GetDecommissionResponse fromXContent(XContentParser parser) throws String currentFieldName = parser.currentName(); if (attributeType.equals(currentFieldName)) { if (parser.nextToken() != XContentParser.Token.START_OBJECT) { - throw new OpenSearchParseException("failed to parse decommission attribute type [{}], expected object", attributeType); + throw new OpenSearchParseException( + "failed to parse decommission attribute type [{}], expected object", + attributeType + ); } token = parser.nextToken(); if (token != XContentParser.Token.END_OBJECT) { @@ -85,7 +88,10 @@ public static GetDecommissionResponse fromXContent(XContentParser parser) throws if (token == XContentParser.Token.VALUE_STRING) { value = parser.text(); } else { - throw new OpenSearchParseException("failed to parse attribute [{}], expected string for attribute value", fieldName); + throw new OpenSearchParseException( + "failed to parse attribute [{}], expected string for attribute value", + fieldName + ); } decommissionAttribute = new DecommissionAttribute(fieldName, value); token = parser.nextToken(); @@ -97,7 +103,9 @@ public static GetDecommissionResponse fromXContent(XContentParser parser) throws } } else if ("status".equals(currentFieldName)) { if (parser.nextToken() != XContentParser.Token.VALUE_STRING) { - throw new OpenSearchParseException("failed to parse status of decommissioning, expected string but found unknown type"); + throw new OpenSearchParseException( + "failed to parse status of decommissioning, expected string but found unknown type" + ); } status = DecommissionStatus.fromString(parser.text()); } else { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/TransportGetDecommissionAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/TransportGetDecommissionAction.java index 6627b78d664f0..1f875d2df96df 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/TransportGetDecommissionAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/TransportGetDecommissionAction.java @@ -26,9 +26,7 @@ import java.io.IOException; -public class TransportGetDecommissionAction extends TransportClusterManagerNodeReadAction< - GetDecommissionRequest, - GetDecommissionResponse> { +public class TransportGetDecommissionAction extends TransportClusterManagerNodeReadAction { @Inject public TransportGetDecommissionAction( @@ -66,14 +64,9 @@ protected void clusterManagerOperation( ActionListener listener ) throws Exception { Metadata metadata = state.metadata(); -// DecommissionAttributeMetadata decommissionedAttributes = metadata.custom(DecommissionAttributeMetadata.TYPE); + // DecommissionAttributeMetadata decommissionedAttributes = metadata.custom(DecommissionAttributeMetadata.TYPE); // TODO - update once service layer changes are merged - listener.onResponse( - new GetDecommissionResponse( - new DecommissionAttribute("zone", "zone-1"), - DecommissionStatus.DECOMMISSIONED - ) - ); + listener.onResponse(new GetDecommissionResponse(new DecommissionAttribute("zone", "zone-1"), DecommissionStatus.DECOMMISSIONED)); } @Override diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequest.java index fd78deb86614b..c570791003199 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequest.java @@ -24,8 +24,7 @@ public class PutDecommissionRequest extends ClusterManagerNodeRequest client.admin() - .cluster() - .getDecommission(getDecommissionRequest, new RestToXContentListener<>(channel)); + return channel -> client.admin().cluster().getDecommission(getDecommissionRequest, new RestToXContentListener<>(channel)); } } diff --git a/server/src/main/java/org/opensearch/rest/action/admin/cluster/RestPutDecommissionAction.java b/server/src/main/java/org/opensearch/rest/action/admin/cluster/RestPutDecommissionAction.java index 9ad6d04703e60..458d73a6e8dd8 100644 --- a/server/src/main/java/org/opensearch/rest/action/admin/cluster/RestPutDecommissionAction.java +++ b/server/src/main/java/org/opensearch/rest/action/admin/cluster/RestPutDecommissionAction.java @@ -8,8 +8,6 @@ package org.opensearch.rest.action.admin.cluster; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.opensearch.action.admin.cluster.decommission.awareness.put.PutDecommissionRequest; import org.opensearch.client.Requests; import org.opensearch.client.node.NodeClient; @@ -42,9 +40,7 @@ public String getName() { @Override protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException { PutDecommissionRequest putDecommissionRequest = createRequest(request); - return channel -> client.admin() - .cluster() - .putDecommission(putDecommissionRequest, new RestToXContentListener<>(channel)); + return channel -> client.admin().cluster().putDecommission(putDecommissionRequest, new RestToXContentListener<>(channel)); } PutDecommissionRequest createRequest(RestRequest request) throws IOException { @@ -58,8 +54,7 @@ PutDecommissionRequest createRequest(RestRequest request) throws IOException { if (request.hasParam("awareness_attribute_value")) { attributeValue = request.param("awareness_attribute_value"); } - return putDecommissionRequest - .setDecommissionAttribute(new DecommissionAttribute(attributeName, attributeValue)) + return putDecommissionRequest.setDecommissionAttribute(new DecommissionAttribute(attributeName, attributeValue)) .setTimeout(TimeValue.parseTimeValue(request.param("timeout"), DEFAULT_TIMEOUT, getClass().getSimpleName() + ".timeout")); } } diff --git a/server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequestTests.java b/server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequestTests.java index 7f1be10232f3e..606158725c955 100644 --- a/server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequestTests.java +++ b/server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequestTests.java @@ -22,16 +22,9 @@ public void testSerialization() throws IOException { String attributeValue = "zone-1"; DecommissionAttribute decommissionAttribute = new DecommissionAttribute(attributeName, attributeValue); TimeValue timeout = TimeValue.timeValueMillis(between(0, 30000)); - final PutDecommissionRequest originalRequest = new PutDecommissionRequest( - decommissionAttribute, - timeout - ); + final PutDecommissionRequest originalRequest = new PutDecommissionRequest(decommissionAttribute, timeout); - final PutDecommissionRequest deserialized = copyWriteable( - originalRequest, - writableRegistry(), - PutDecommissionRequest::new - ); + final PutDecommissionRequest deserialized = copyWriteable(originalRequest, writableRegistry(), PutDecommissionRequest::new); assertEquals(deserialized.getDecommissionAttribute(), originalRequest.getDecommissionAttribute()); assertEquals(deserialized.getTimeout(), originalRequest.getTimeout()); @@ -44,10 +37,7 @@ public void testValidation() { DecommissionAttribute decommissionAttribute = new DecommissionAttribute(attributeName, attributeValue); TimeValue timeout = TimeValue.timeValueMillis(between(0, 30000)); - final PutDecommissionRequest request = new PutDecommissionRequest( - decommissionAttribute, - timeout - ); + final PutDecommissionRequest request = new PutDecommissionRequest(decommissionAttribute, timeout); ActionRequestValidationException e = request.validate(); assertNotNull(e); assertTrue(e.getMessage().contains("attribute name is missing")); @@ -58,10 +48,7 @@ public void testValidation() { DecommissionAttribute decommissionAttribute = new DecommissionAttribute(attributeName, attributeValue); TimeValue timeout = TimeValue.timeValueMillis(between(0, 30000)); - final PutDecommissionRequest request = new PutDecommissionRequest( - decommissionAttribute, - timeout - ); + final PutDecommissionRequest request = new PutDecommissionRequest(decommissionAttribute, timeout); ActionRequestValidationException e = request.validate(); assertNotNull(e); assertTrue(e.getMessage().contains("attribute value is missing")); @@ -72,10 +59,7 @@ public void testValidation() { DecommissionAttribute decommissionAttribute = new DecommissionAttribute(attributeName, attributeValue); TimeValue timeout = TimeValue.timeValueMillis(between(0, 30000)); - final PutDecommissionRequest request = new PutDecommissionRequest( - decommissionAttribute, - timeout - ); + final PutDecommissionRequest request = new PutDecommissionRequest(decommissionAttribute, timeout); ActionRequestValidationException e = request.validate(); assertNull(e); } From e0f65de827f01262b991887a7eaf08631d08ddb3 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Mon, 22 Aug 2022 17:25:21 +0530 Subject: [PATCH 080/187] Add temp URL Signed-off-by: Rishab Nahata --- .../rest-api-spec/api/cluster.get_decommission_awareness.json | 4 ++-- .../rest-api-spec/api/cluster.put_decommission_awareness.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.get_decommission_awareness.json b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.get_decommission_awareness.json index b5aa1787fd87c..19cb03aaf20c4 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.get_decommission_awareness.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.get_decommission_awareness.json @@ -1,8 +1,8 @@ { "cluster.get_decommission_awareness": { "documentation": { - "url": "TBA", - "description": "TBA" + "url": "https://opensearch.org/docs/latest/opensearch/rest-api/decommission/", + "description": "Get details and status of decommissioned attribute" }, "stability": "experimental", "url": { diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.put_decommission_awareness.json b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.put_decommission_awareness.json index 3dfd0f50851f3..815964e49e7a0 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.put_decommission_awareness.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.put_decommission_awareness.json @@ -1,8 +1,8 @@ { "cluster.put_decommission_awareness": { "documentation": { - "url": "TBA", - "description": "TBA" + "url": "https://opensearch.org/docs/latest/opensearch/rest-api/decommission/", + "description": "Decommissions an awareness attribute" }, "stability": "experimental", "url": { From 755aa4651bdf6bb9676190adb66e98d466cb906c Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Mon, 22 Aug 2022 17:55:21 +0530 Subject: [PATCH 081/187] Bug fix Signed-off-by: Rishab Nahata --- .../java/org/opensearch/client/RestHighLevelClientTests.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/RestHighLevelClientTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/RestHighLevelClientTests.java index cdd63743f2644..1384e99019793 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/RestHighLevelClientTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/RestHighLevelClientTests.java @@ -887,7 +887,9 @@ public void testApiNamingConventions() throws Exception { "nodes.usage", "nodes.reload_secure_settings", "search_shards", - "remote_store.restore", }; + "remote_store.restore", + "cluster.put_decommission_awareness", + "cluster.get_decommission_awareness", }; List booleanReturnMethods = Arrays.asList("security.enable_user", "security.disable_user", "security.change_password"); Set deprecatedMethods = new HashSet<>(); deprecatedMethods.add("indices.force_merge"); From 944ff3d0756769c23a81100cc3c4d8bc61a646d6 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 23 Aug 2022 20:06:18 +0530 Subject: [PATCH 082/187] Fix Signed-off-by: Rishab Nahata --- .../awareness/get/GetDecommissionResponse.java | 12 ++++++++++-- .../get/TransportGetDecommissionAction.java | 9 +++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponse.java index 343868894620a..8826bd5d479d5 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponse.java @@ -28,6 +28,10 @@ public class GetDecommissionResponse extends ActionResponse implements ToXConten private final DecommissionAttribute decommissionedAttribute; private final DecommissionStatus status; + GetDecommissionResponse() { + this(null, null); + } + GetDecommissionResponse(DecommissionAttribute decommissionedAttribute, DecommissionStatus status) { this.decommissionedAttribute = decommissionedAttribute; this.status = status; @@ -56,9 +60,13 @@ public DecommissionStatus getDecommissionStatus() { public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(); builder.startObject("awareness"); - builder.field(decommissionedAttribute.attributeName(), decommissionedAttribute.attributeValue()); + if (decommissionedAttribute != null) { + builder.field(decommissionedAttribute.attributeName(), decommissionedAttribute.attributeValue()); + } builder.endObject(); - builder.field("status", status); + if (status!=null) { + builder.field("status", status); + } builder.endObject(); return builder; } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/TransportGetDecommissionAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/TransportGetDecommissionAction.java index 1f875d2df96df..74d2c3363eb52 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/TransportGetDecommissionAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/TransportGetDecommissionAction.java @@ -66,7 +66,16 @@ protected void clusterManagerOperation( Metadata metadata = state.metadata(); // DecommissionAttributeMetadata decommissionedAttributes = metadata.custom(DecommissionAttributeMetadata.TYPE); // TODO - update once service layer changes are merged +//<<<<<<< HEAD listener.onResponse(new GetDecommissionResponse(new DecommissionAttribute("zone", "zone-1"), DecommissionStatus.DECOMMISSIONED)); +//======= +// if (decommissionedAttributes!=null) { +// listener.onResponse(new GetDecommissionResponse(decommissionedAttributes.decommissionAttribute(), decommissionedAttributes.status())); +// } +// else { +// listener.onResponse(new GetDecommissionResponse()); +// } +//>>>>>>> 1025b6e3e3e (Fix GET without PUT) } @Override From b7f4f791106a797b6cad5316a4e0382c63d61383 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Thu, 25 Aug 2022 15:33:51 +0530 Subject: [PATCH 083/187] Fixes Signed-off-by: Rishab Nahata --- .../decommission/awareness/get/GetDecommissionResponse.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponse.java index 8826bd5d479d5..7b971049ac4b5 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponse.java @@ -28,10 +28,6 @@ public class GetDecommissionResponse extends ActionResponse implements ToXConten private final DecommissionAttribute decommissionedAttribute; private final DecommissionStatus status; - GetDecommissionResponse() { - this(null, null); - } - GetDecommissionResponse(DecommissionAttribute decommissionedAttribute, DecommissionStatus status) { this.decommissionedAttribute = decommissionedAttribute; this.status = status; From 219bafc3d1304b6dbac5228ee2cb6550a1f738d0 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Thu, 25 Aug 2022 16:22:42 +0530 Subject: [PATCH 084/187] Fix spotless and precommit checks Signed-off-by: Rishab Nahata --- .../get/GetDecommissionResponse.java | 2 +- .../get/TransportGetDecommissionAction.java | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponse.java index 7b971049ac4b5..21b5140035f7d 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponse.java @@ -60,7 +60,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws builder.field(decommissionedAttribute.attributeName(), decommissionedAttribute.attributeValue()); } builder.endObject(); - if (status!=null) { + if (status != null) { builder.field("status", status); } builder.endObject(); diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/TransportGetDecommissionAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/TransportGetDecommissionAction.java index 74d2c3363eb52..70d56bd73b341 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/TransportGetDecommissionAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/TransportGetDecommissionAction.java @@ -66,16 +66,17 @@ protected void clusterManagerOperation( Metadata metadata = state.metadata(); // DecommissionAttributeMetadata decommissionedAttributes = metadata.custom(DecommissionAttributeMetadata.TYPE); // TODO - update once service layer changes are merged -//<<<<<<< HEAD + // <<<<<<< HEAD listener.onResponse(new GetDecommissionResponse(new DecommissionAttribute("zone", "zone-1"), DecommissionStatus.DECOMMISSIONED)); -//======= -// if (decommissionedAttributes!=null) { -// listener.onResponse(new GetDecommissionResponse(decommissionedAttributes.decommissionAttribute(), decommissionedAttributes.status())); -// } -// else { -// listener.onResponse(new GetDecommissionResponse()); -// } -//>>>>>>> 1025b6e3e3e (Fix GET without PUT) + // ======= + // if (decommissionedAttributes!=null) { + // listener.onResponse(new GetDecommissionResponse(decommissionedAttributes.decommissionAttribute(), + // decommissionedAttributes.status())); + // } + // else { + // listener.onResponse(new GetDecommissionResponse()); + // } + // >>>>>>> 1025b6e3e3e (Fix GET without PUT) } @Override From d12587ff8ffe40d95f7d35c152acfe7918bbfc22 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Thu, 1 Sep 2022 15:33:44 +0530 Subject: [PATCH 085/187] Add java docs and fix test Signed-off-by: Rishab Nahata --- .../awareness/get/GetDecommissionAction.java | 5 +++++ .../awareness/get/GetDecommissionRequestBuilder.java | 5 +++++ .../awareness/get/GetDecommissionResponse.java | 5 +++++ .../awareness/get/TransportGetDecommissionAction.java | 5 +++++ .../decommission/awareness/get/package-info.java | 10 ++++++++++ .../cluster/decommission/awareness/package-info.java | 10 ++++++++++ .../awareness/put/PutDecommissionAction.java | 5 +++++ .../awareness/put/PutDecommissionRequest.java | 7 +++++++ .../awareness/put/PutDecommissionRequestBuilder.java | 5 +++++ .../awareness/put/PutDecommissionResponse.java | 5 +++++ .../awareness/put/TransportPutDecommissionAction.java | 5 +++++ .../decommission/awareness/put/package-info.java | 10 ++++++++++ .../cluster/decommission/DecommissionAttribute.java | 5 +++++ .../cluster/decommission/DecommissionStatus.java | 3 +++ .../opensearch/cluster/decommission/package-info.java | 10 ++++++++++ .../admin/cluster/RestPutDecommissionAction.java | 5 +++++ .../admin/cluster/RestPutDecommissionActionTests.java | 4 +++- 17 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/package-info.java create mode 100644 server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/package-info.java create mode 100644 server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/package-info.java create mode 100644 server/src/main/java/org/opensearch/cluster/decommission/package-info.java diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionAction.java index 21e75c1dfee3e..72a7d5d53ce77 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionAction.java @@ -10,6 +10,11 @@ import org.opensearch.action.ActionType; +/** + * Get decommission action + * + * @opensearch.internal + */ public class GetDecommissionAction extends ActionType { public static final GetDecommissionAction INSTANCE = new GetDecommissionAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionRequestBuilder.java index 08a702cdc7524..c6a5925d87860 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionRequestBuilder.java @@ -11,6 +11,11 @@ import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; +/** + * Get decommission request builder + * + * @opensearch.internal + */ public class GetDecommissionRequestBuilder extends ClusterManagerNodeReadOperationRequestBuilder< GetDecommissionRequest, GetDecommissionResponse, diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponse.java index 21b5140035f7d..29c307065a700 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponse.java @@ -23,6 +23,11 @@ import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken; +/** + * Response for decommission status + * + * @opensearch.internal + */ public class GetDecommissionResponse extends ActionResponse implements ToXContentObject { private final DecommissionAttribute decommissionedAttribute; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/TransportGetDecommissionAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/TransportGetDecommissionAction.java index 70d56bd73b341..28d91fd454d20 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/TransportGetDecommissionAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/TransportGetDecommissionAction.java @@ -26,6 +26,11 @@ import java.io.IOException; +/** + * Transport action for getting decommission status + * + * @opensearch.internal + */ public class TransportGetDecommissionAction extends TransportClusterManagerNodeReadAction { @Inject diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/package-info.java new file mode 100644 index 0000000000000..5b88e91cf4f9d --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Transport handlers for getting status of decommission request */ +package org.opensearch.action.admin.cluster.decommission.awareness.get; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/package-info.java new file mode 100644 index 0000000000000..e1260e638c91d --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Decommission transport handlers. */ +package org.opensearch.action.admin.cluster.decommission.awareness; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionAction.java index 5e033f7d0b355..78d6270247c97 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionAction.java @@ -10,6 +10,11 @@ import org.opensearch.action.ActionType; +/** + * Register decommission action + * + * @opensearch.internal + */ public final class PutDecommissionAction extends ActionType { public static final PutDecommissionAction INSTANCE = new PutDecommissionAction(); public static final String NAME = "cluster:admin/decommission/awareness/put"; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequest.java index c570791003199..d92e2c002a0c8 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequest.java @@ -19,6 +19,13 @@ import static org.opensearch.action.ValidateActions.addValidationError; +/** + * Register decommission request. + *

+ * Registers a decommission request with decommission attribute and timeout + * + * @opensearch.internal + */ public class PutDecommissionRequest extends ClusterManagerNodeRequest { private DecommissionAttribute decommissionAttribute; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequestBuilder.java index 3ff874865382c..300ed202df98c 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequestBuilder.java @@ -14,6 +14,11 @@ import org.opensearch.cluster.decommission.DecommissionAttribute; import org.opensearch.common.unit.TimeValue; +/** + * Register decommission request builder + * + * @opensearch.internal + */ public class PutDecommissionRequestBuilder extends ClusterManagerNodeOperationRequestBuilder< PutDecommissionRequest, PutDecommissionResponse, diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionResponse.java index 695e894aaa886..c584c039e2d93 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionResponse.java @@ -15,6 +15,11 @@ import java.io.IOException; +/** + * Response for decommission request + * + * @opensearch.internal + */ public class PutDecommissionResponse extends AcknowledgedResponse implements ToXContentObject { public PutDecommissionResponse(StreamInput in) throws IOException { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/TransportPutDecommissionAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/TransportPutDecommissionAction.java index d813378d15c62..08769e575fa5e 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/TransportPutDecommissionAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/TransportPutDecommissionAction.java @@ -25,6 +25,11 @@ import java.io.IOException; +/** + * Transport action for registering decommission + * + * @opensearch.internal + */ public class TransportPutDecommissionAction extends TransportClusterManagerNodeAction { private static final Logger logger = LogManager.getLogger(TransportPutDecommissionAction.class); diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/package-info.java new file mode 100644 index 0000000000000..c361f4b95a484 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Transport handlers for putting a new decommission request */ +package org.opensearch.action.admin.cluster.decommission.awareness.put; diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java index 7abcd18ac7c38..fa8512f11e5a7 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java @@ -15,6 +15,11 @@ import java.io.IOException; import java.util.Objects; +/** + * {@link DecommissionAttribute} encapsulates information about decommissioned node attribute like attribute name, attribute value. + * + * @opensearch.internal + */ public final class DecommissionAttribute implements Writeable { private final String attributeName; private final String attributeValue; diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java index 208bddac3cf29..a7c0a4253a103 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java @@ -8,6 +8,9 @@ package org.opensearch.cluster.decommission; +/** + * An enumeration of the states during decommissioning and recommissioning. + */ public enum DecommissionStatus { /** * Decommission process is initiated diff --git a/server/src/main/java/org/opensearch/cluster/decommission/package-info.java b/server/src/main/java/org/opensearch/cluster/decommission/package-info.java new file mode 100644 index 0000000000000..090177aacf237 --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/decommission/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Files in this package will be merged as part of another PR#4084 */ +package org.opensearch.cluster.decommission; diff --git a/server/src/main/java/org/opensearch/rest/action/admin/cluster/RestPutDecommissionAction.java b/server/src/main/java/org/opensearch/rest/action/admin/cluster/RestPutDecommissionAction.java index 458d73a6e8dd8..9155634c59239 100644 --- a/server/src/main/java/org/opensearch/rest/action/admin/cluster/RestPutDecommissionAction.java +++ b/server/src/main/java/org/opensearch/rest/action/admin/cluster/RestPutDecommissionAction.java @@ -23,6 +23,11 @@ import static java.util.Collections.singletonList; import static org.opensearch.rest.RestRequest.Method.PUT; +/** + * Registers decommission action + * + * @opensearch.api + */ public class RestPutDecommissionAction extends BaseRestHandler { private static final TimeValue DEFAULT_TIMEOUT = TimeValue.timeValueSeconds(300L); diff --git a/server/src/test/java/org/opensearch/rest/action/admin/cluster/RestPutDecommissionActionTests.java b/server/src/test/java/org/opensearch/rest/action/admin/cluster/RestPutDecommissionActionTests.java index f5ff48e787637..71be108e1637d 100644 --- a/server/src/test/java/org/opensearch/rest/action/admin/cluster/RestPutDecommissionActionTests.java +++ b/server/src/test/java/org/opensearch/rest/action/admin/cluster/RestPutDecommissionActionTests.java @@ -41,6 +41,7 @@ public void testCreateRequest() throws IOException { assertEquals(request.getDecommissionAttribute().attributeName(), "zone"); assertEquals(request.getDecommissionAttribute().attributeValue(), "zone-1"); assertEquals(request.getTimeout(), TimeValue.timeValueSeconds(10L)); + assertEquals(deprecatedRequest.getHttpRequest().method(), RestRequest.Method.PUT); } public void testCreateRequestWithDefaultTimeout() throws IOException { @@ -54,10 +55,11 @@ public void testCreateRequestWithDefaultTimeout() throws IOException { assertEquals(request.getDecommissionAttribute().attributeName(), "zone"); assertEquals(request.getDecommissionAttribute().attributeValue(), "zone-1"); assertEquals(request.getTimeout(), TimeValue.timeValueSeconds(300L)); + assertEquals(deprecatedRequest.getHttpRequest().method(), RestRequest.Method.PUT); } private FakeRestRequest buildRestRequest(Map params) { - return new FakeRestRequest.Builder(xContentRegistry()).withMethod(RestRequest.Method.GET) + return new FakeRestRequest.Builder(xContentRegistry()).withMethod(RestRequest.Method.PUT) .withPath("/_cluster/decommission/awareness/{awareness_attribute_name}/{awareness_attribute_value}") .withParams(params) .build(); From c5e42e5354f99595fe5dc9afdd69834f1c69f47e Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Thu, 1 Sep 2022 15:39:09 +0530 Subject: [PATCH 086/187] Add changelog Signed-off-by: Rishab Nahata --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f89e7eba0698c..f8a49033a7499 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Added release notes for 2.2.1 ([#4344](https://github.com/opensearch-project/OpenSearch/pull/4344)) - Label configuration for dependabot PRs ([#4348](https://github.com/opensearch-project/OpenSearch/pull/4348)) - Support for HTTP/2 (server-side) ([#3847](https://github.com/opensearch-project/OpenSearch/pull/3847)) +- Add APIs (GET/PUT) to decommission awareness attribute ([#4261](https://github.com/opensearch-project/OpenSearch/pull/4261)) ### Changed - Dependency updates (httpcore, mockito, slf4j, httpasyncclient, commons-codec) ([#4308](https://github.com/opensearch-project/OpenSearch/pull/4308)) @@ -29,7 +30,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Add timeout on Mockito.verify to reduce flakyness in testReplicationOnDone test([#4314](https://github.com/opensearch-project/OpenSearch/pull/4314)) - Commit workflow for dependabot changelog helper ([#4331](https://github.com/opensearch-project/OpenSearch/pull/4331)) - Fixed cancellation of segment replication events ([#4225](https://github.com/opensearch-project/OpenSearch/pull/4225)) -- Bugs for dependabot changelog verifier workflow ([#4364](https://github.com/opensearch-project/OpenSearch/pull/4364)) +- Bugs for dependabot changelog verifier workflow ([#4364](https://github.com/opensearch-project/OpenSearch/pull/4364)) ### Security - CVE-2022-25857 org.yaml:snakeyaml DOS vulnerability ([#4341](https://github.com/opensearch-project/OpenSearch/pull/4341)) From 425f0b7ecb96061b08d16c4b569656d65b32b6ee Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Thu, 1 Sep 2022 17:59:23 +0530 Subject: [PATCH 087/187] Empty-Commit Signed-off-by: Rishab Nahata From bd11c6958a78f57ce4de33ef5d004044bf838622 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 08:24:08 -0700 Subject: [PATCH 088/187] Bump com.diffplug.spotless from 6.9.1 to 6.10.0 (#4319) --- CHANGELOG.md | 4 +++- build.gradle | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f89e7eba0698c..19b5c8e85cfeb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Added release notes for 2.2.1 ([#4344](https://github.com/opensearch-project/OpenSearch/pull/4344)) - Label configuration for dependabot PRs ([#4348](https://github.com/opensearch-project/OpenSearch/pull/4348)) - Support for HTTP/2 (server-side) ([#3847](https://github.com/opensearch-project/OpenSearch/pull/3847)) +### Dependencies +- Bumps `com.diffplug.spotless` from 6.9.1 to 6.10.0 ### Changed - Dependency updates (httpcore, mockito, slf4j, httpasyncclient, commons-codec) ([#4308](https://github.com/opensearch-project/OpenSearch/pull/4308)) @@ -53,4 +55,4 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) [Unreleased]: https://github.com/opensearch-project/OpenSearch/compare/2.2.0...HEAD -[2.x]: https://github.com/opensearch-project/OpenSearch/compare/2.2.0...2.x +[2.x]: https://github.com/opensearch-project/OpenSearch/compare/2.2.0...2.x \ No newline at end of file diff --git a/build.gradle b/build.gradle index ce5ea6cdd7e11..a1f4f2d04883a 100644 --- a/build.gradle +++ b/build.gradle @@ -55,7 +55,7 @@ plugins { id 'lifecycle-base' id 'opensearch.docker-support' id 'opensearch.global-build-info' - id "com.diffplug.spotless" version "6.9.1" apply false + id "com.diffplug.spotless" version "6.10.0" apply false id "org.gradle.test-retry" version "1.4.0" apply false id "test-report-aggregation" id 'jacoco-report-aggregation' From 236f2f611051bd50aaa9fcb2ccbaa4bd6211fea0 Mon Sep 17 00:00:00 2001 From: Andriy Redko Date: Thu, 1 Sep 2022 11:28:35 -0400 Subject: [PATCH 089/187] Update to Netty 4.1.80.Final (#4359) Signed-off-by: Andriy Redko --- CHANGELOG.md | 1 + buildSrc/version.properties | 2 +- modules/transport-netty4/build.gradle | 8 ++++++++ .../licenses/netty-buffer-4.1.79.Final.jar.sha1 | 1 - .../licenses/netty-buffer-4.1.80.Final.jar.sha1 | 1 + .../licenses/netty-codec-4.1.79.Final.jar.sha1 | 1 - .../licenses/netty-codec-4.1.80.Final.jar.sha1 | 1 + .../licenses/netty-codec-http-4.1.79.Final.jar.sha1 | 1 - .../licenses/netty-codec-http-4.1.80.Final.jar.sha1 | 1 + .../licenses/netty-codec-http2-4.1.79.Final.jar.sha1 | 1 - .../licenses/netty-codec-http2-4.1.80.Final.jar.sha1 | 1 + .../licenses/netty-common-4.1.79.Final.jar.sha1 | 1 - .../licenses/netty-common-4.1.80.Final.jar.sha1 | 1 + .../licenses/netty-handler-4.1.79.Final.jar.sha1 | 1 - .../licenses/netty-handler-4.1.80.Final.jar.sha1 | 1 + .../licenses/netty-resolver-4.1.79.Final.jar.sha1 | 1 - .../licenses/netty-resolver-4.1.80.Final.jar.sha1 | 1 + .../licenses/netty-transport-4.1.79.Final.jar.sha1 | 1 - .../licenses/netty-transport-4.1.80.Final.jar.sha1 | 1 + ...tty-transport-native-unix-common-4.1.79.Final.jar.sha1 | 1 - ...tty-transport-native-unix-common-4.1.80.Final.jar.sha1 | 1 + .../licenses/netty-codec-dns-4.1.79.Final.jar.sha1 | 1 - .../licenses/netty-codec-dns-4.1.80.Final.jar.sha1 | 1 + .../licenses/netty-codec-http2-4.1.79.Final.jar.sha1 | 1 - .../licenses/netty-codec-http2-4.1.80.Final.jar.sha1 | 1 + .../licenses/netty-codec-socks-4.1.79.Final.jar.sha1 | 1 - .../licenses/netty-codec-socks-4.1.80.Final.jar.sha1 | 1 + .../licenses/netty-handler-proxy-4.1.79.Final.jar.sha1 | 1 - .../licenses/netty-handler-proxy-4.1.80.Final.jar.sha1 | 1 + .../licenses/netty-resolver-dns-4.1.79.Final.jar.sha1 | 1 - .../licenses/netty-resolver-dns-4.1.80.Final.jar.sha1 | 1 + ...tty-transport-native-unix-common-4.1.79.Final.jar.sha1 | 1 - ...tty-transport-native-unix-common-4.1.80.Final.jar.sha1 | 1 + .../licenses/netty-all-4.1.79.Final.jar.sha1 | 1 - .../licenses/netty-all-4.1.80.Final.jar.sha1 | 1 + plugins/transport-nio/build.gradle | 6 ++++++ .../licenses/netty-buffer-4.1.79.Final.jar.sha1 | 1 - .../licenses/netty-buffer-4.1.80.Final.jar.sha1 | 1 + .../licenses/netty-codec-4.1.79.Final.jar.sha1 | 1 - .../licenses/netty-codec-4.1.80.Final.jar.sha1 | 1 + .../licenses/netty-codec-http-4.1.79.Final.jar.sha1 | 1 - .../licenses/netty-codec-http-4.1.80.Final.jar.sha1 | 1 + .../licenses/netty-common-4.1.79.Final.jar.sha1 | 1 - .../licenses/netty-common-4.1.80.Final.jar.sha1 | 1 + .../licenses/netty-handler-4.1.79.Final.jar.sha1 | 1 - .../licenses/netty-handler-4.1.80.Final.jar.sha1 | 1 + .../licenses/netty-resolver-4.1.79.Final.jar.sha1 | 1 - .../licenses/netty-resolver-4.1.80.Final.jar.sha1 | 1 + .../licenses/netty-transport-4.1.79.Final.jar.sha1 | 1 - .../licenses/netty-transport-4.1.80.Final.jar.sha1 | 1 + 50 files changed, 39 insertions(+), 24 deletions(-) delete mode 100644 modules/transport-netty4/licenses/netty-buffer-4.1.79.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-buffer-4.1.80.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-codec-4.1.79.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-codec-4.1.80.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-codec-http-4.1.79.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-codec-http-4.1.80.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-codec-http2-4.1.79.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-codec-http2-4.1.80.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-common-4.1.79.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-common-4.1.80.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-handler-4.1.79.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-handler-4.1.80.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-resolver-4.1.79.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-resolver-4.1.80.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-transport-4.1.79.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-transport-4.1.80.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-transport-native-unix-common-4.1.79.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-transport-native-unix-common-4.1.80.Final.jar.sha1 delete mode 100644 plugins/repository-azure/licenses/netty-codec-dns-4.1.79.Final.jar.sha1 create mode 100644 plugins/repository-azure/licenses/netty-codec-dns-4.1.80.Final.jar.sha1 delete mode 100644 plugins/repository-azure/licenses/netty-codec-http2-4.1.79.Final.jar.sha1 create mode 100644 plugins/repository-azure/licenses/netty-codec-http2-4.1.80.Final.jar.sha1 delete mode 100644 plugins/repository-azure/licenses/netty-codec-socks-4.1.79.Final.jar.sha1 create mode 100644 plugins/repository-azure/licenses/netty-codec-socks-4.1.80.Final.jar.sha1 delete mode 100644 plugins/repository-azure/licenses/netty-handler-proxy-4.1.79.Final.jar.sha1 create mode 100644 plugins/repository-azure/licenses/netty-handler-proxy-4.1.80.Final.jar.sha1 delete mode 100644 plugins/repository-azure/licenses/netty-resolver-dns-4.1.79.Final.jar.sha1 create mode 100644 plugins/repository-azure/licenses/netty-resolver-dns-4.1.80.Final.jar.sha1 delete mode 100644 plugins/repository-azure/licenses/netty-transport-native-unix-common-4.1.79.Final.jar.sha1 create mode 100644 plugins/repository-azure/licenses/netty-transport-native-unix-common-4.1.80.Final.jar.sha1 delete mode 100644 plugins/repository-hdfs/licenses/netty-all-4.1.79.Final.jar.sha1 create mode 100644 plugins/repository-hdfs/licenses/netty-all-4.1.80.Final.jar.sha1 delete mode 100644 plugins/transport-nio/licenses/netty-buffer-4.1.79.Final.jar.sha1 create mode 100644 plugins/transport-nio/licenses/netty-buffer-4.1.80.Final.jar.sha1 delete mode 100644 plugins/transport-nio/licenses/netty-codec-4.1.79.Final.jar.sha1 create mode 100644 plugins/transport-nio/licenses/netty-codec-4.1.80.Final.jar.sha1 delete mode 100644 plugins/transport-nio/licenses/netty-codec-http-4.1.79.Final.jar.sha1 create mode 100644 plugins/transport-nio/licenses/netty-codec-http-4.1.80.Final.jar.sha1 delete mode 100644 plugins/transport-nio/licenses/netty-common-4.1.79.Final.jar.sha1 create mode 100644 plugins/transport-nio/licenses/netty-common-4.1.80.Final.jar.sha1 delete mode 100644 plugins/transport-nio/licenses/netty-handler-4.1.79.Final.jar.sha1 create mode 100644 plugins/transport-nio/licenses/netty-handler-4.1.80.Final.jar.sha1 delete mode 100644 plugins/transport-nio/licenses/netty-resolver-4.1.79.Final.jar.sha1 create mode 100644 plugins/transport-nio/licenses/netty-resolver-4.1.80.Final.jar.sha1 delete mode 100644 plugins/transport-nio/licenses/netty-transport-4.1.79.Final.jar.sha1 create mode 100644 plugins/transport-nio/licenses/netty-transport-4.1.80.Final.jar.sha1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 19b5c8e85cfeb..6434041b038ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Dependency updates (httpcore, mockito, slf4j, httpasyncclient, commons-codec) ([#4308](https://github.com/opensearch-project/OpenSearch/pull/4308)) - Use RemoteSegmentStoreDirectory instead of RemoteDirectory ([#4240](https://github.com/opensearch-project/OpenSearch/pull/4240)) - Plugin ZIP publication groupId value is configurable ([#4156](https://github.com/opensearch-project/OpenSearch/pull/4156)) +- Update to Netty 4.1.80.Final ([#4359](https://github.com/opensearch-project/OpenSearch/pull/4359)) ### Deprecated diff --git a/buildSrc/version.properties b/buildSrc/version.properties index 072dcc4578977..6cc24a3f09244 100644 --- a/buildSrc/version.properties +++ b/buildSrc/version.properties @@ -21,7 +21,7 @@ asm = 9.3 # when updating the JNA version, also update the version in buildSrc/build.gradle jna = 5.5.0 -netty = 4.1.79.Final +netty = 4.1.80.Final joda = 2.10.13 # client dependencies diff --git a/modules/transport-netty4/build.gradle b/modules/transport-netty4/build.gradle index 5d2047d7f18a2..8bbe0bf2ef65f 100644 --- a/modules/transport-netty4/build.gradle +++ b/modules/transport-netty4/build.gradle @@ -144,6 +144,14 @@ thirdPartyAudit { 'org.apache.log4j.Level', 'org.apache.log4j.Logger', + // from io.netty.handler.ssl.OpenSslEngine (netty) + 'org.bouncycastle.openssl.PEMEncryptedKeyPair', + 'org.bouncycastle.openssl.PEMParser', + 'org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter', + 'org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder', + 'org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder', + 'org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo', + // from io.netty.handler.ssl.OpenSslEngine (netty) 'io.netty.internal.tcnative.Buffer', 'io.netty.internal.tcnative.CertificateCompressionAlgo', diff --git a/modules/transport-netty4/licenses/netty-buffer-4.1.79.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-buffer-4.1.79.Final.jar.sha1 deleted file mode 100644 index 8e9e4d0b7f754..0000000000000 --- a/modules/transport-netty4/licenses/netty-buffer-4.1.79.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -6c014412b599489b1db27c6bc08d8a46da94e397 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-buffer-4.1.80.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-buffer-4.1.80.Final.jar.sha1 new file mode 100644 index 0000000000000..471fe8b211df2 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-buffer-4.1.80.Final.jar.sha1 @@ -0,0 +1 @@ +a087321a63d9991e25f7b7d24ef53edcbcb954ff \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-codec-4.1.79.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-codec-4.1.79.Final.jar.sha1 deleted file mode 100644 index c0920231d79a8..0000000000000 --- a/modules/transport-netty4/licenses/netty-codec-4.1.79.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -18f5b02af7ca611978bc28f2cb58cbb3b9b0f0ef \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-codec-4.1.80.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-codec-4.1.80.Final.jar.sha1 new file mode 100644 index 0000000000000..0f8e3bebe1532 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-codec-4.1.80.Final.jar.sha1 @@ -0,0 +1 @@ +4941821a158d16311665d8606aefa610ecf0f64c \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-codec-http-4.1.79.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-codec-http-4.1.79.Final.jar.sha1 deleted file mode 100644 index a3f650da5abbd..0000000000000 --- a/modules/transport-netty4/licenses/netty-codec-http-4.1.79.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -882c70bc0a30a98bf3ce477f043e967ac026044c \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-codec-http-4.1.80.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-codec-http-4.1.80.Final.jar.sha1 new file mode 100644 index 0000000000000..d18720d164335 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-codec-http-4.1.80.Final.jar.sha1 @@ -0,0 +1 @@ +efb23f9d5187d2f733595ef7930137f0cb2cec48 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-codec-http2-4.1.79.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-codec-http2-4.1.79.Final.jar.sha1 deleted file mode 100644 index f2989024cfce1..0000000000000 --- a/modules/transport-netty4/licenses/netty-codec-http2-4.1.79.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -0eeffab0cd5efb699d5e4ab9b694d32fef6694b3 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-codec-http2-4.1.80.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-codec-http2-4.1.80.Final.jar.sha1 new file mode 100644 index 0000000000000..d96a286b98493 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-codec-http2-4.1.80.Final.jar.sha1 @@ -0,0 +1 @@ +bf7b66834188ef1a6f6095291c6b81a1880798ba \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-common-4.1.79.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-common-4.1.79.Final.jar.sha1 deleted file mode 100644 index faa7b099406a3..0000000000000 --- a/modules/transport-netty4/licenses/netty-common-4.1.79.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -2814bd465731355323aba0fdd22163bfce638a75 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-common-4.1.80.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-common-4.1.80.Final.jar.sha1 new file mode 100644 index 0000000000000..d256e77b7024c --- /dev/null +++ b/modules/transport-netty4/licenses/netty-common-4.1.80.Final.jar.sha1 @@ -0,0 +1 @@ +3d43ce22863bc590e4e33fbdabbb58dc05f4c43d \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-handler-4.1.79.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-handler-4.1.79.Final.jar.sha1 deleted file mode 100644 index 8e314f164da69..0000000000000 --- a/modules/transport-netty4/licenses/netty-handler-4.1.79.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -2dc22423c8ed19906615fb936a5fcb7db14a4e6c \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-handler-4.1.80.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-handler-4.1.80.Final.jar.sha1 new file mode 100644 index 0000000000000..022ad6bc93dba --- /dev/null +++ b/modules/transport-netty4/licenses/netty-handler-4.1.80.Final.jar.sha1 @@ -0,0 +1 @@ +cf7029d2f9bc4eeae8ff15af7a528d06b518a017 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-resolver-4.1.79.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-resolver-4.1.79.Final.jar.sha1 deleted file mode 100644 index af550935bb911..0000000000000 --- a/modules/transport-netty4/licenses/netty-resolver-4.1.79.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -55ecb1ff4464b56564a90824a741c3911264aaa4 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-resolver-4.1.80.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-resolver-4.1.80.Final.jar.sha1 new file mode 100644 index 0000000000000..ad0f71b569377 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-resolver-4.1.80.Final.jar.sha1 @@ -0,0 +1 @@ +3bbb0d4bfbbab867e5b757b97a6e5e0d1348d94c \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-transport-4.1.79.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-transport-4.1.79.Final.jar.sha1 deleted file mode 100644 index c6e18efb3ad3d..0000000000000 --- a/modules/transport-netty4/licenses/netty-transport-4.1.79.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -6cc2b49749b4fbcc39c687027e04e65e857552a9 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-transport-4.1.80.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-transport-4.1.80.Final.jar.sha1 new file mode 100644 index 0000000000000..2bfb4f377d89b --- /dev/null +++ b/modules/transport-netty4/licenses/netty-transport-4.1.80.Final.jar.sha1 @@ -0,0 +1 @@ +57fcace7a1b8567aa39921c915d1b1ba78fd4d2d \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-transport-native-unix-common-4.1.79.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-transport-native-unix-common-4.1.79.Final.jar.sha1 deleted file mode 100644 index 7f984663dfa85..0000000000000 --- a/modules/transport-netty4/licenses/netty-transport-native-unix-common-4.1.79.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -731937caec938b77b39df932a8da8aaca8d5ec05 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-transport-native-unix-common-4.1.80.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-transport-native-unix-common-4.1.80.Final.jar.sha1 new file mode 100644 index 0000000000000..998e6e8560724 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-transport-native-unix-common-4.1.80.Final.jar.sha1 @@ -0,0 +1 @@ +da3d7da1a8d317ae2c82b400fd255fe610c43ebe \ No newline at end of file diff --git a/plugins/repository-azure/licenses/netty-codec-dns-4.1.79.Final.jar.sha1 b/plugins/repository-azure/licenses/netty-codec-dns-4.1.79.Final.jar.sha1 deleted file mode 100644 index a1753b194ea31..0000000000000 --- a/plugins/repository-azure/licenses/netty-codec-dns-4.1.79.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -6c19c46f9529791964f636c93cfaca0556f0d5d0 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/netty-codec-dns-4.1.80.Final.jar.sha1 b/plugins/repository-azure/licenses/netty-codec-dns-4.1.80.Final.jar.sha1 new file mode 100644 index 0000000000000..2dab7f40b02b7 --- /dev/null +++ b/plugins/repository-azure/licenses/netty-codec-dns-4.1.80.Final.jar.sha1 @@ -0,0 +1 @@ +6926d2ea779f41071ecb1948d880dfbb3a6ee126 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/netty-codec-http2-4.1.79.Final.jar.sha1 b/plugins/repository-azure/licenses/netty-codec-http2-4.1.79.Final.jar.sha1 deleted file mode 100644 index f2989024cfce1..0000000000000 --- a/plugins/repository-azure/licenses/netty-codec-http2-4.1.79.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -0eeffab0cd5efb699d5e4ab9b694d32fef6694b3 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/netty-codec-http2-4.1.80.Final.jar.sha1 b/plugins/repository-azure/licenses/netty-codec-http2-4.1.80.Final.jar.sha1 new file mode 100644 index 0000000000000..d96a286b98493 --- /dev/null +++ b/plugins/repository-azure/licenses/netty-codec-http2-4.1.80.Final.jar.sha1 @@ -0,0 +1 @@ +bf7b66834188ef1a6f6095291c6b81a1880798ba \ No newline at end of file diff --git a/plugins/repository-azure/licenses/netty-codec-socks-4.1.79.Final.jar.sha1 b/plugins/repository-azure/licenses/netty-codec-socks-4.1.79.Final.jar.sha1 deleted file mode 100644 index 913f0e7685c86..0000000000000 --- a/plugins/repository-azure/licenses/netty-codec-socks-4.1.79.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -794a5937cdb1871c4ae350610752dec2929dc1d6 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/netty-codec-socks-4.1.80.Final.jar.sha1 b/plugins/repository-azure/licenses/netty-codec-socks-4.1.80.Final.jar.sha1 new file mode 100644 index 0000000000000..625344e6cfb0a --- /dev/null +++ b/plugins/repository-azure/licenses/netty-codec-socks-4.1.80.Final.jar.sha1 @@ -0,0 +1 @@ +00025b767be3425f3b31a34ee095c85619169f17 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/netty-handler-proxy-4.1.79.Final.jar.sha1 b/plugins/repository-azure/licenses/netty-handler-proxy-4.1.79.Final.jar.sha1 deleted file mode 100644 index dbb072f3f665f..0000000000000 --- a/plugins/repository-azure/licenses/netty-handler-proxy-4.1.79.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -054aace8683de7893cf28d4aab72cd60f49b5700 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/netty-handler-proxy-4.1.80.Final.jar.sha1 b/plugins/repository-azure/licenses/netty-handler-proxy-4.1.80.Final.jar.sha1 new file mode 100644 index 0000000000000..c3184ec5ff7d3 --- /dev/null +++ b/plugins/repository-azure/licenses/netty-handler-proxy-4.1.80.Final.jar.sha1 @@ -0,0 +1 @@ +9b3b42ff805723fb98120f5ab2019c53e71da91b \ No newline at end of file diff --git a/plugins/repository-azure/licenses/netty-resolver-dns-4.1.79.Final.jar.sha1 b/plugins/repository-azure/licenses/netty-resolver-dns-4.1.79.Final.jar.sha1 deleted file mode 100644 index a5d1be00d9c29..0000000000000 --- a/plugins/repository-azure/licenses/netty-resolver-dns-4.1.79.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -8eb9be9b6a66a03f5f4df67fe559cb676493d167 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/netty-resolver-dns-4.1.80.Final.jar.sha1 b/plugins/repository-azure/licenses/netty-resolver-dns-4.1.80.Final.jar.sha1 new file mode 100644 index 0000000000000..bb6a3502a729f --- /dev/null +++ b/plugins/repository-azure/licenses/netty-resolver-dns-4.1.80.Final.jar.sha1 @@ -0,0 +1 @@ +6b1602f80b6235b0b7d53bc5e9c1a6cd11c1b804 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/netty-transport-native-unix-common-4.1.79.Final.jar.sha1 b/plugins/repository-azure/licenses/netty-transport-native-unix-common-4.1.79.Final.jar.sha1 deleted file mode 100644 index 7f984663dfa85..0000000000000 --- a/plugins/repository-azure/licenses/netty-transport-native-unix-common-4.1.79.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -731937caec938b77b39df932a8da8aaca8d5ec05 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/netty-transport-native-unix-common-4.1.80.Final.jar.sha1 b/plugins/repository-azure/licenses/netty-transport-native-unix-common-4.1.80.Final.jar.sha1 new file mode 100644 index 0000000000000..998e6e8560724 --- /dev/null +++ b/plugins/repository-azure/licenses/netty-transport-native-unix-common-4.1.80.Final.jar.sha1 @@ -0,0 +1 @@ +da3d7da1a8d317ae2c82b400fd255fe610c43ebe \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/netty-all-4.1.79.Final.jar.sha1 b/plugins/repository-hdfs/licenses/netty-all-4.1.79.Final.jar.sha1 deleted file mode 100644 index 724950db96f09..0000000000000 --- a/plugins/repository-hdfs/licenses/netty-all-4.1.79.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -1c53cffaa14d61de523b167377843e35807292a7 \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/netty-all-4.1.80.Final.jar.sha1 b/plugins/repository-hdfs/licenses/netty-all-4.1.80.Final.jar.sha1 new file mode 100644 index 0000000000000..ae6eb1d85f1ea --- /dev/null +++ b/plugins/repository-hdfs/licenses/netty-all-4.1.80.Final.jar.sha1 @@ -0,0 +1 @@ +39e73b76a3ec65df731b371179e15f2c3e4e7575 \ No newline at end of file diff --git a/plugins/transport-nio/build.gradle b/plugins/transport-nio/build.gradle index a7e8c42a4e2d3..c5b401de60c8c 100644 --- a/plugins/transport-nio/build.gradle +++ b/plugins/transport-nio/build.gradle @@ -83,6 +83,12 @@ thirdPartyAudit { 'org.bouncycastle.cert.X509v3CertificateBuilder', 'org.bouncycastle.cert.jcajce.JcaX509CertificateConverter', 'org.bouncycastle.operator.jcajce.JcaContentSignerBuilder', + 'org.bouncycastle.openssl.PEMEncryptedKeyPair', + 'org.bouncycastle.openssl.PEMParser', + 'org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter', + 'org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder', + 'org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder', + 'org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo', // from io.netty.handler.ssl.JettyNpnSslEngine (netty) 'org.eclipse.jetty.npn.NextProtoNego$ClientProvider', diff --git a/plugins/transport-nio/licenses/netty-buffer-4.1.79.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-buffer-4.1.79.Final.jar.sha1 deleted file mode 100644 index 8e9e4d0b7f754..0000000000000 --- a/plugins/transport-nio/licenses/netty-buffer-4.1.79.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -6c014412b599489b1db27c6bc08d8a46da94e397 \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-buffer-4.1.80.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-buffer-4.1.80.Final.jar.sha1 new file mode 100644 index 0000000000000..471fe8b211df2 --- /dev/null +++ b/plugins/transport-nio/licenses/netty-buffer-4.1.80.Final.jar.sha1 @@ -0,0 +1 @@ +a087321a63d9991e25f7b7d24ef53edcbcb954ff \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-codec-4.1.79.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-codec-4.1.79.Final.jar.sha1 deleted file mode 100644 index c0920231d79a8..0000000000000 --- a/plugins/transport-nio/licenses/netty-codec-4.1.79.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -18f5b02af7ca611978bc28f2cb58cbb3b9b0f0ef \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-codec-4.1.80.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-codec-4.1.80.Final.jar.sha1 new file mode 100644 index 0000000000000..0f8e3bebe1532 --- /dev/null +++ b/plugins/transport-nio/licenses/netty-codec-4.1.80.Final.jar.sha1 @@ -0,0 +1 @@ +4941821a158d16311665d8606aefa610ecf0f64c \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-codec-http-4.1.79.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-codec-http-4.1.79.Final.jar.sha1 deleted file mode 100644 index a3f650da5abbd..0000000000000 --- a/plugins/transport-nio/licenses/netty-codec-http-4.1.79.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -882c70bc0a30a98bf3ce477f043e967ac026044c \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-codec-http-4.1.80.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-codec-http-4.1.80.Final.jar.sha1 new file mode 100644 index 0000000000000..d18720d164335 --- /dev/null +++ b/plugins/transport-nio/licenses/netty-codec-http-4.1.80.Final.jar.sha1 @@ -0,0 +1 @@ +efb23f9d5187d2f733595ef7930137f0cb2cec48 \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-common-4.1.79.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-common-4.1.79.Final.jar.sha1 deleted file mode 100644 index faa7b099406a3..0000000000000 --- a/plugins/transport-nio/licenses/netty-common-4.1.79.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -2814bd465731355323aba0fdd22163bfce638a75 \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-common-4.1.80.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-common-4.1.80.Final.jar.sha1 new file mode 100644 index 0000000000000..d256e77b7024c --- /dev/null +++ b/plugins/transport-nio/licenses/netty-common-4.1.80.Final.jar.sha1 @@ -0,0 +1 @@ +3d43ce22863bc590e4e33fbdabbb58dc05f4c43d \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-handler-4.1.79.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-handler-4.1.79.Final.jar.sha1 deleted file mode 100644 index 8e314f164da69..0000000000000 --- a/plugins/transport-nio/licenses/netty-handler-4.1.79.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -2dc22423c8ed19906615fb936a5fcb7db14a4e6c \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-handler-4.1.80.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-handler-4.1.80.Final.jar.sha1 new file mode 100644 index 0000000000000..022ad6bc93dba --- /dev/null +++ b/plugins/transport-nio/licenses/netty-handler-4.1.80.Final.jar.sha1 @@ -0,0 +1 @@ +cf7029d2f9bc4eeae8ff15af7a528d06b518a017 \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-resolver-4.1.79.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-resolver-4.1.79.Final.jar.sha1 deleted file mode 100644 index af550935bb911..0000000000000 --- a/plugins/transport-nio/licenses/netty-resolver-4.1.79.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -55ecb1ff4464b56564a90824a741c3911264aaa4 \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-resolver-4.1.80.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-resolver-4.1.80.Final.jar.sha1 new file mode 100644 index 0000000000000..ad0f71b569377 --- /dev/null +++ b/plugins/transport-nio/licenses/netty-resolver-4.1.80.Final.jar.sha1 @@ -0,0 +1 @@ +3bbb0d4bfbbab867e5b757b97a6e5e0d1348d94c \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-transport-4.1.79.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-transport-4.1.79.Final.jar.sha1 deleted file mode 100644 index c6e18efb3ad3d..0000000000000 --- a/plugins/transport-nio/licenses/netty-transport-4.1.79.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -6cc2b49749b4fbcc39c687027e04e65e857552a9 \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-transport-4.1.80.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-transport-4.1.80.Final.jar.sha1 new file mode 100644 index 0000000000000..2bfb4f377d89b --- /dev/null +++ b/plugins/transport-nio/licenses/netty-transport-4.1.80.Final.jar.sha1 @@ -0,0 +1 @@ +57fcace7a1b8567aa39921c915d1b1ba78fd4d2d \ No newline at end of file From 5c3cc935c79b0b1eb48826a3182805d6c51bafea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 09:15:10 -0700 Subject: [PATCH 090/187] Bump xmlbeans from 5.1.0 to 5.1.1 in /plugins/ingest-attachment (#4354) * Bump xmlbeans from 5.1.0 to 5.1.1 in /plugins/ingest-attachment Bumps xmlbeans from 5.1.0 to 5.1.1. --- updated-dependencies: - dependency-name: org.apache.xmlbeans:xmlbeans dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * Updating SHAs Signed-off-by: dependabot[bot] * Update changelog Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: dependabot[bot] --- CHANGELOG.md | 1 + plugins/ingest-attachment/build.gradle | 2 +- plugins/ingest-attachment/licenses/xmlbeans-5.1.0.jar.sha1 | 1 - plugins/ingest-attachment/licenses/xmlbeans-5.1.1.jar.sha1 | 1 + 4 files changed, 3 insertions(+), 2 deletions(-) delete mode 100644 plugins/ingest-attachment/licenses/xmlbeans-5.1.0.jar.sha1 create mode 100644 plugins/ingest-attachment/licenses/xmlbeans-5.1.1.jar.sha1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 6434041b038ee..93aef24a46674 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Support for HTTP/2 (server-side) ([#3847](https://github.com/opensearch-project/OpenSearch/pull/3847)) ### Dependencies - Bumps `com.diffplug.spotless` from 6.9.1 to 6.10.0 +- Bumps `xmlbeans` from 5.1.0 to 5.1.1 ### Changed - Dependency updates (httpcore, mockito, slf4j, httpasyncclient, commons-codec) ([#4308](https://github.com/opensearch-project/OpenSearch/pull/4308)) diff --git a/plugins/ingest-attachment/build.gradle b/plugins/ingest-attachment/build.gradle index 86694b9bc9da7..af9485c991f0c 100644 --- a/plugins/ingest-attachment/build.gradle +++ b/plugins/ingest-attachment/build.gradle @@ -79,7 +79,7 @@ dependencies { api "org.apache.poi:poi:${versions.poi}" api "org.apache.poi:poi-ooxml-lite:${versions.poi}" api "commons-codec:commons-codec:${versions.commonscodec}" - api 'org.apache.xmlbeans:xmlbeans:5.1.0' + api 'org.apache.xmlbeans:xmlbeans:5.1.1' api 'org.apache.commons:commons-collections4:4.4' // MS Office api "org.apache.poi:poi-scratchpad:${versions.poi}" diff --git a/plugins/ingest-attachment/licenses/xmlbeans-5.1.0.jar.sha1 b/plugins/ingest-attachment/licenses/xmlbeans-5.1.0.jar.sha1 deleted file mode 100644 index 85f757b61048c..0000000000000 --- a/plugins/ingest-attachment/licenses/xmlbeans-5.1.0.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -3534ab896663e6f6d8a2cf46882d7407641d7a31 \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/xmlbeans-5.1.1.jar.sha1 b/plugins/ingest-attachment/licenses/xmlbeans-5.1.1.jar.sha1 new file mode 100644 index 0000000000000..4d1d2ad0807e7 --- /dev/null +++ b/plugins/ingest-attachment/licenses/xmlbeans-5.1.1.jar.sha1 @@ -0,0 +1 @@ +48a369df0eccb509d46203104e4df9cb00f0f68b \ No newline at end of file From 715da849e0bf910c84a7c7615ab2faccef95b4a7 Mon Sep 17 00:00:00 2001 From: Rabi Panda Date: Thu, 1 Sep 2022 10:06:19 -0700 Subject: [PATCH 091/187] Fix randomized test failure NRTReplicationEngineTests.testUpdateSegments (#4352) Overload `generateHistoryOnReplica` to be able to generate only a specific `Engine.Operation.TYPE` operations as required by the `testUpdateSegments` test Signed-off-by: Rabi Panda Signed-off-by: Rabi Panda --- CHANGELOG.md | 3 ++- .../index/engine/NRTReplicationEngineTests.java | 12 ++++++++---- .../opensearch/index/engine/EngineTestCase.java | 17 ++++++++++++++++- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 93aef24a46674..376c8f37c8063 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,7 +33,8 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Add timeout on Mockito.verify to reduce flakyness in testReplicationOnDone test([#4314](https://github.com/opensearch-project/OpenSearch/pull/4314)) - Commit workflow for dependabot changelog helper ([#4331](https://github.com/opensearch-project/OpenSearch/pull/4331)) - Fixed cancellation of segment replication events ([#4225](https://github.com/opensearch-project/OpenSearch/pull/4225)) -- Bugs for dependabot changelog verifier workflow ([#4364](https://github.com/opensearch-project/OpenSearch/pull/4364)) +- Bugs for dependabot changelog verifier workflow ([#4364](https://github.com/opensearch-project/OpenSearch/pull/4364)) +- Fix flaky random test `NRTReplicationEngineTests.testUpdateSegments` ([#4352](https://github.com/opensearch-project/OpenSearch/pull/4352)) ### Security - CVE-2022-25857 org.yaml:snakeyaml DOS vulnerability ([#4341](https://github.com/opensearch-project/OpenSearch/pull/4341)) diff --git a/server/src/test/java/org/opensearch/index/engine/NRTReplicationEngineTests.java b/server/src/test/java/org/opensearch/index/engine/NRTReplicationEngineTests.java index 1fe1a37dedae0..0008afcc901c7 100644 --- a/server/src/test/java/org/opensearch/index/engine/NRTReplicationEngineTests.java +++ b/server/src/test/java/org/opensearch/index/engine/NRTReplicationEngineTests.java @@ -112,10 +112,14 @@ public void testUpdateSegments() throws Exception { final NRTReplicationEngine nrtEngine = buildNrtReplicaEngine(globalCheckpoint, nrtEngineStore) ) { // add docs to the primary engine. - List operations = generateHistoryOnReplica(between(1, 500), randomBoolean(), randomBoolean(), randomBoolean()) - .stream() - .filter(op -> op.operationType().equals(Engine.Operation.TYPE.INDEX)) - .collect(Collectors.toList()); + List operations = generateHistoryOnReplica( + between(1, 500), + randomBoolean(), + randomBoolean(), + randomBoolean(), + Engine.Operation.TYPE.INDEX + ); + for (Engine.Operation op : operations) { applyOperation(engine, op); applyOperation(nrtEngine, op); diff --git a/test/framework/src/main/java/org/opensearch/index/engine/EngineTestCase.java b/test/framework/src/main/java/org/opensearch/index/engine/EngineTestCase.java index 174747d306ff5..af754d77560cc 100644 --- a/test/framework/src/main/java/org/opensearch/index/engine/EngineTestCase.java +++ b/test/framework/src/main/java/org/opensearch/index/engine/EngineTestCase.java @@ -1070,6 +1070,22 @@ public List generateHistoryOnReplica( boolean allowGapInSeqNo, boolean allowDuplicate, boolean includeNestedDocs + ) throws Exception { + return generateHistoryOnReplica( + numOps, + allowGapInSeqNo, + allowDuplicate, + includeNestedDocs, + randomFrom(Engine.Operation.TYPE.values()) + ); + } + + public List generateHistoryOnReplica( + int numOps, + boolean allowGapInSeqNo, + boolean allowDuplicate, + boolean includeNestedDocs, + Engine.Operation.TYPE opType ) throws Exception { long seqNo = 0; final int maxIdValue = randomInt(numOps * 2); @@ -1077,7 +1093,6 @@ public List generateHistoryOnReplica( CheckedBiFunction nestedParsedDocFactory = nestedParsedDocFactory(); for (int i = 0; i < numOps; i++) { final String id = Integer.toString(randomInt(maxIdValue)); - final Engine.Operation.TYPE opType = randomFrom(Engine.Operation.TYPE.values()); final boolean isNestedDoc = includeNestedDocs && opType == Engine.Operation.TYPE.INDEX && randomBoolean(); final int nestedValues = between(0, 3); final long startTime = threadPool.relativeTimeInNanos(); From 70d911cad98e843f4702673015abce508190d389 Mon Sep 17 00:00:00 2001 From: "opensearch-trigger-bot[bot]" <98922864+opensearch-trigger-bot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 21:26:36 -0700 Subject: [PATCH 092/187] [AUTO] [main] Added bwc version 2.2.2. (#4383) * Added bwc version 2.2.2 * Add changelog Signed-off-by: Kunal Kotwani Signed-off-by: Kunal Kotwani Co-authored-by: opensearch-ci-bot Co-authored-by: Kunal Kotwani --- .ci/bwcVersions | 1 + CHANGELOG.md | 4 +++- server/src/main/java/org/opensearch/Version.java | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.ci/bwcVersions b/.ci/bwcVersions index 1ba3ee562317a..914426eebe35e 100644 --- a/.ci/bwcVersions +++ b/.ci/bwcVersions @@ -49,4 +49,5 @@ BWC_VERSION: - "2.1.1" - "2.2.0" - "2.2.1" + - "2.2.2" - "2.3.0" diff --git a/CHANGELOG.md b/CHANGELOG.md index 376c8f37c8063..76f134f10c29e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Added release notes for 2.2.1 ([#4344](https://github.com/opensearch-project/OpenSearch/pull/4344)) - Label configuration for dependabot PRs ([#4348](https://github.com/opensearch-project/OpenSearch/pull/4348)) - Support for HTTP/2 (server-side) ([#3847](https://github.com/opensearch-project/OpenSearch/pull/3847)) +- BWC version 2.2.2 ([#4383](https://github.com/opensearch-project/OpenSearch/pull/4383)) + ### Dependencies - Bumps `com.diffplug.spotless` from 6.9.1 to 6.10.0 - Bumps `xmlbeans` from 5.1.0 to 5.1.1 @@ -58,4 +60,4 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) [Unreleased]: https://github.com/opensearch-project/OpenSearch/compare/2.2.0...HEAD -[2.x]: https://github.com/opensearch-project/OpenSearch/compare/2.2.0...2.x \ No newline at end of file +[2.x]: https://github.com/opensearch-project/OpenSearch/compare/2.2.0...2.x diff --git a/server/src/main/java/org/opensearch/Version.java b/server/src/main/java/org/opensearch/Version.java index ba512d3fbcdd9..10e5f16419a7a 100644 --- a/server/src/main/java/org/opensearch/Version.java +++ b/server/src/main/java/org/opensearch/Version.java @@ -96,6 +96,7 @@ public class Version implements Comparable, ToXContentFragment { public static final Version V_2_1_1 = new Version(2010199, org.apache.lucene.util.Version.LUCENE_9_2_0); public static final Version V_2_2_0 = new Version(2020099, org.apache.lucene.util.Version.LUCENE_9_3_0); public static final Version V_2_2_1 = new Version(2020199, org.apache.lucene.util.Version.LUCENE_9_3_0); + public static final Version V_2_2_2 = new Version(2020299, org.apache.lucene.util.Version.LUCENE_9_3_0); public static final Version V_2_3_0 = new Version(2030099, org.apache.lucene.util.Version.LUCENE_9_3_0); public static final Version V_3_0_0 = new Version(3000099, org.apache.lucene.util.Version.LUCENE_9_4_0); public static final Version CURRENT = V_3_0_0; From 5557f596481db845aad02101f85bc35233e0ae5b Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Fri, 2 Sep 2022 16:27:01 +0530 Subject: [PATCH 093/187] Rename PutDecommissionAction to DecommissionAction Signed-off-by: Rishab Nahata --- .../org/opensearch/action/ActionModule.java | 10 ++++----- ...ionAction.java => DecommissionAction.java} | 8 +++---- ...nRequest.java => DecommissionRequest.java} | 14 ++++++------ ...r.java => DecommissionRequestBuilder.java} | 18 +++++++-------- ...esponse.java => DecommissionResponse.java} | 6 ++--- ....java => TransportDecommissionAction.java} | 22 +++++++++---------- .../opensearch/client/ClusterAdminClient.java | 12 +++++----- .../java/org/opensearch/client/Requests.java | 6 ++--- .../client/support/AbstractClient.java | 20 ++++++++--------- ...ction.java => RestDecommissionAction.java} | 14 ++++++------ ...sts.java => DecommissionRequestTests.java} | 12 +++++----- ...ts.java => DecommissionResponseTests.java} | 6 ++--- ....java => RestDecommissionActionTests.java} | 12 +++++----- 13 files changed, 80 insertions(+), 80 deletions(-) rename server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/{PutDecommissionAction.java => DecommissionAction.java} (64%) rename server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/{PutDecommissionRequest.java => DecommissionRequest.java} (82%) rename server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/{PutDecommissionRequestBuilder.java => DecommissionRequestBuilder.java} (68%) rename server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/{PutDecommissionResponse.java => DecommissionResponse.java} (77%) rename server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/{TransportPutDecommissionAction.java => TransportDecommissionAction.java} (77%) rename server/src/main/java/org/opensearch/rest/action/admin/cluster/{RestPutDecommissionAction.java => RestDecommissionAction.java} (75%) rename server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/put/{PutDecommissionRequestTests.java => DecommissionRequestTests.java} (78%) rename server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/put/{PutDecommissionResponseTests.java => DecommissionResponseTests.java} (66%) rename server/src/test/java/org/opensearch/rest/action/admin/cluster/{RestPutDecommissionActionTests.java => RestDecommissionActionTests.java} (86%) diff --git a/server/src/main/java/org/opensearch/action/ActionModule.java b/server/src/main/java/org/opensearch/action/ActionModule.java index 900d50a155c92..fd389cb69cc0e 100644 --- a/server/src/main/java/org/opensearch/action/ActionModule.java +++ b/server/src/main/java/org/opensearch/action/ActionModule.java @@ -42,8 +42,8 @@ import org.opensearch.action.admin.cluster.configuration.TransportClearVotingConfigExclusionsAction; import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionAction; import org.opensearch.action.admin.cluster.decommission.awareness.get.TransportGetDecommissionAction; -import org.opensearch.action.admin.cluster.decommission.awareness.put.PutDecommissionAction; -import org.opensearch.action.admin.cluster.decommission.awareness.put.TransportPutDecommissionAction; +import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionAction; +import org.opensearch.action.admin.cluster.decommission.awareness.put.TransportDecommissionAction; import org.opensearch.action.admin.cluster.health.ClusterHealthAction; import org.opensearch.action.admin.cluster.health.TransportClusterHealthAction; import org.opensearch.action.admin.cluster.node.hotthreads.NodesHotThreadsAction; @@ -322,7 +322,7 @@ import org.opensearch.rest.action.admin.cluster.RestNodesStatsAction; import org.opensearch.rest.action.admin.cluster.RestNodesUsageAction; import org.opensearch.rest.action.admin.cluster.RestPendingClusterTasksAction; -import org.opensearch.rest.action.admin.cluster.RestPutDecommissionAction; +import org.opensearch.rest.action.admin.cluster.RestDecommissionAction; import org.opensearch.rest.action.admin.cluster.RestPutRepositoryAction; import org.opensearch.rest.action.admin.cluster.RestPutStoredScriptAction; import org.opensearch.rest.action.admin.cluster.RestReloadSecureSettingsAction; @@ -690,7 +690,7 @@ public void reg actions.register(RestoreRemoteStoreAction.INSTANCE, TransportRestoreRemoteStoreAction.class); // Decommission actions - actions.register(PutDecommissionAction.INSTANCE, TransportPutDecommissionAction.class); + actions.register(DecommissionAction.INSTANCE, TransportDecommissionAction.class); actions.register(GetDecommissionAction.INSTANCE, TransportGetDecommissionAction.class); return unmodifiableMap(actions.getRegistry()); @@ -883,7 +883,7 @@ public void initRestHandlers(Supplier nodesInCluster) { } } registerHandler.accept(new RestCatAction(catActions)); - registerHandler.accept(new RestPutDecommissionAction()); + registerHandler.accept(new RestDecommissionAction()); registerHandler.accept(new RestGetDecommissionAction()); // Remote Store APIs diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/DecommissionAction.java similarity index 64% rename from server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionAction.java rename to server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/DecommissionAction.java index 78d6270247c97..56678650f6e35 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/DecommissionAction.java @@ -15,11 +15,11 @@ * * @opensearch.internal */ -public final class PutDecommissionAction extends ActionType { - public static final PutDecommissionAction INSTANCE = new PutDecommissionAction(); +public final class DecommissionAction extends ActionType { + public static final DecommissionAction INSTANCE = new DecommissionAction(); public static final String NAME = "cluster:admin/decommission/awareness/put"; - private PutDecommissionAction() { - super(NAME, PutDecommissionResponse::new); + private DecommissionAction() { + super(NAME, DecommissionResponse::new); } } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/DecommissionRequest.java similarity index 82% rename from server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequest.java rename to server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/DecommissionRequest.java index d92e2c002a0c8..f835f9368c41e 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/DecommissionRequest.java @@ -26,19 +26,19 @@ * * @opensearch.internal */ -public class PutDecommissionRequest extends ClusterManagerNodeRequest { +public class DecommissionRequest extends ClusterManagerNodeRequest { private DecommissionAttribute decommissionAttribute; private TimeValue timeout; - public PutDecommissionRequest() {} + public DecommissionRequest() {} - public PutDecommissionRequest(DecommissionAttribute decommissionAttribute, TimeValue timeout) { + public DecommissionRequest(DecommissionAttribute decommissionAttribute, TimeValue timeout) { this.decommissionAttribute = decommissionAttribute; this.timeout = timeout; } - public PutDecommissionRequest(StreamInput in) throws IOException { + public DecommissionRequest(StreamInput in) throws IOException { super(in); decommissionAttribute = new DecommissionAttribute(in); timeout = in.readTimeValue(); @@ -57,7 +57,7 @@ public void writeTo(StreamOutput out) throws IOException { * @param decommissionAttribute attribute key-value that needs to be decommissioned * @return this request */ - public PutDecommissionRequest setDecommissionAttribute(DecommissionAttribute decommissionAttribute) { + public DecommissionRequest setDecommissionAttribute(DecommissionAttribute decommissionAttribute) { this.decommissionAttribute = decommissionAttribute; return this; } @@ -68,7 +68,7 @@ public PutDecommissionRequest setDecommissionAttribute(DecommissionAttribute dec * @param timeout time out for the request * @return this request */ - public PutDecommissionRequest setTimeout(TimeValue timeout) { + public DecommissionRequest setTimeout(TimeValue timeout) { this.timeout = timeout; return this; } @@ -98,6 +98,6 @@ public ActionRequestValidationException validate() { @Override public String toString() { - return "PutDecommissionRequest{" + "decommissionAttribute=" + decommissionAttribute + ", timeout=" + timeout + '}'; + return "DecommissionRequest{" + "decommissionAttribute=" + decommissionAttribute + ", timeout=" + timeout + '}'; } } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/DecommissionRequestBuilder.java similarity index 68% rename from server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequestBuilder.java rename to server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/DecommissionRequestBuilder.java index 300ed202df98c..daeddf08b1684 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/DecommissionRequestBuilder.java @@ -19,15 +19,15 @@ * * @opensearch.internal */ -public class PutDecommissionRequestBuilder extends ClusterManagerNodeOperationRequestBuilder< - PutDecommissionRequest, - PutDecommissionResponse, - PutDecommissionRequestBuilder> { +public class DecommissionRequestBuilder extends ClusterManagerNodeOperationRequestBuilder< + DecommissionRequest, + DecommissionResponse, + DecommissionRequestBuilder> { - public PutDecommissionRequestBuilder( + public DecommissionRequestBuilder( OpenSearchClient client, - ActionType action, - PutDecommissionRequest request + ActionType action, + DecommissionRequest request ) { super(client, action, request); } @@ -36,7 +36,7 @@ public PutDecommissionRequestBuilder( * @param decommissionAttribute decommission attribute * @return current object */ - public PutDecommissionRequestBuilder setDecommissionedAttribute(DecommissionAttribute decommissionAttribute) { + public DecommissionRequestBuilder setDecommissionedAttribute(DecommissionAttribute decommissionAttribute) { request.setDecommissionAttribute(decommissionAttribute); return this; } @@ -45,7 +45,7 @@ public PutDecommissionRequestBuilder setDecommissionedAttribute(DecommissionAttr * @param timeout time out for the request * @return current object */ - public PutDecommissionRequestBuilder setTimeout(TimeValue timeout) { + public DecommissionRequestBuilder setTimeout(TimeValue timeout) { request.setTimeout(timeout); return this; } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/DecommissionResponse.java similarity index 77% rename from server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionResponse.java rename to server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/DecommissionResponse.java index c584c039e2d93..499f403c8cd64 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/DecommissionResponse.java @@ -20,13 +20,13 @@ * * @opensearch.internal */ -public class PutDecommissionResponse extends AcknowledgedResponse implements ToXContentObject { +public class DecommissionResponse extends AcknowledgedResponse implements ToXContentObject { - public PutDecommissionResponse(StreamInput in) throws IOException { + public DecommissionResponse(StreamInput in) throws IOException { super(in); } - public PutDecommissionResponse(boolean acknowledged) { + public DecommissionResponse(boolean acknowledged) { super(acknowledged); } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/TransportPutDecommissionAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/TransportDecommissionAction.java similarity index 77% rename from server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/TransportPutDecommissionAction.java rename to server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/TransportDecommissionAction.java index 08769e575fa5e..f07d562f36d93 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/TransportPutDecommissionAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/TransportDecommissionAction.java @@ -30,12 +30,12 @@ * * @opensearch.internal */ -public class TransportPutDecommissionAction extends TransportClusterManagerNodeAction { +public class TransportDecommissionAction extends TransportClusterManagerNodeAction { - private static final Logger logger = LogManager.getLogger(TransportPutDecommissionAction.class); + private static final Logger logger = LogManager.getLogger(TransportDecommissionAction.class); @Inject - public TransportPutDecommissionAction( + public TransportDecommissionAction( TransportService transportService, ClusterService clusterService, // DecommissionService decommissionService, @@ -44,12 +44,12 @@ public TransportPutDecommissionAction( IndexNameExpressionResolver indexNameExpressionResolver ) { super( - PutDecommissionAction.NAME, + DecommissionAction.NAME, transportService, clusterService, threadPool, actionFilters, - PutDecommissionRequest::new, + DecommissionRequest::new, indexNameExpressionResolver ); // TODO - uncomment when integrating with the service @@ -62,23 +62,23 @@ protected String executor() { } @Override - protected PutDecommissionResponse read(StreamInput in) throws IOException { - return new PutDecommissionResponse(in); + protected DecommissionResponse read(StreamInput in) throws IOException { + return new DecommissionResponse(in); } @Override - protected ClusterBlockException checkBlock(PutDecommissionRequest request, ClusterState state) { + protected ClusterBlockException checkBlock(DecommissionRequest request, ClusterState state) { return state.blocks().globalBlockedException(ClusterBlockLevel.METADATA_WRITE); } @Override protected void clusterManagerOperation( - PutDecommissionRequest request, + DecommissionRequest request, ClusterState state, - ActionListener listener + ActionListener listener ) throws Exception { logger.info("initiating awareness attribute [{}] decommissioning", request.getDecommissionAttribute().toString()); - listener.onResponse(new PutDecommissionResponse(true)); // TODO - remove after integration + listener.onResponse(new DecommissionResponse(true)); // TODO - remove after integration // TODO - uncomment when integrating with the service // decommissionService.initiateAttributeDecommissioning( // request.getDecommissionAttribute(), diff --git a/server/src/main/java/org/opensearch/client/ClusterAdminClient.java b/server/src/main/java/org/opensearch/client/ClusterAdminClient.java index 0c5a7c2b30809..eca6b0bd8a5d9 100644 --- a/server/src/main/java/org/opensearch/client/ClusterAdminClient.java +++ b/server/src/main/java/org/opensearch/client/ClusterAdminClient.java @@ -40,9 +40,9 @@ import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionRequest; import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionRequestBuilder; import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionResponse; -import org.opensearch.action.admin.cluster.decommission.awareness.put.PutDecommissionRequest; -import org.opensearch.action.admin.cluster.decommission.awareness.put.PutDecommissionRequestBuilder; -import org.opensearch.action.admin.cluster.decommission.awareness.put.PutDecommissionResponse; +import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionRequest; +import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionRequestBuilder; +import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionResponse; import org.opensearch.action.admin.cluster.health.ClusterHealthRequest; import org.opensearch.action.admin.cluster.health.ClusterHealthRequestBuilder; import org.opensearch.action.admin.cluster.health.ClusterHealthResponse; @@ -801,17 +801,17 @@ public interface ClusterAdminClient extends OpenSearchClient { /** * Decommission a node */ - ActionFuture putDecommission(PutDecommissionRequest request); + ActionFuture decommission(DecommissionRequest request); /** * Decommission a node */ - void putDecommission(PutDecommissionRequest request, ActionListener listener); + void decommission(DecommissionRequest request, ActionListener listener); /** * Decommission a node */ - PutDecommissionRequestBuilder preparePutDecommission(PutDecommissionRequest request); + DecommissionRequestBuilder prepareDecommission(DecommissionRequest request); /** * Get Decommissioned attribute diff --git a/server/src/main/java/org/opensearch/client/Requests.java b/server/src/main/java/org/opensearch/client/Requests.java index ee97d0412a7c9..a0eac23fc9d98 100644 --- a/server/src/main/java/org/opensearch/client/Requests.java +++ b/server/src/main/java/org/opensearch/client/Requests.java @@ -33,7 +33,7 @@ package org.opensearch.client; import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionRequest; -import org.opensearch.action.admin.cluster.decommission.awareness.put.PutDecommissionRequest; +import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionRequest; import org.opensearch.action.admin.cluster.health.ClusterHealthRequest; import org.opensearch.action.admin.cluster.node.info.NodesInfoRequest; import org.opensearch.action.admin.cluster.node.stats.NodesStatsRequest; @@ -556,8 +556,8 @@ public static SnapshotsStatusRequest snapshotsStatusRequest(String repository) { * * @return returns put decommission request */ - public static PutDecommissionRequest putDecommissionRequest() { - return new PutDecommissionRequest(); + public static DecommissionRequest decommissionRequest() { + return new DecommissionRequest(); } /** diff --git a/server/src/main/java/org/opensearch/client/support/AbstractClient.java b/server/src/main/java/org/opensearch/client/support/AbstractClient.java index c120b24b1efd9..00ec2f5086b12 100644 --- a/server/src/main/java/org/opensearch/client/support/AbstractClient.java +++ b/server/src/main/java/org/opensearch/client/support/AbstractClient.java @@ -47,10 +47,10 @@ import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionRequest; import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionRequestBuilder; import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionResponse; -import org.opensearch.action.admin.cluster.decommission.awareness.put.PutDecommissionAction; -import org.opensearch.action.admin.cluster.decommission.awareness.put.PutDecommissionRequest; -import org.opensearch.action.admin.cluster.decommission.awareness.put.PutDecommissionRequestBuilder; -import org.opensearch.action.admin.cluster.decommission.awareness.put.PutDecommissionResponse; +import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionAction; +import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionRequest; +import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionRequestBuilder; +import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionResponse; import org.opensearch.action.admin.cluster.health.ClusterHealthAction; import org.opensearch.action.admin.cluster.health.ClusterHealthRequest; import org.opensearch.action.admin.cluster.health.ClusterHealthRequestBuilder; @@ -1324,18 +1324,18 @@ public DeleteStoredScriptRequestBuilder prepareDeleteStoredScript(String id) { } @Override - public ActionFuture putDecommission(PutDecommissionRequest request) { - return execute(PutDecommissionAction.INSTANCE, request); + public ActionFuture decommission(DecommissionRequest request) { + return execute(DecommissionAction.INSTANCE, request); } @Override - public void putDecommission(PutDecommissionRequest request, ActionListener listener) { - execute(PutDecommissionAction.INSTANCE, request, listener); + public void decommission(DecommissionRequest request, ActionListener listener) { + execute(DecommissionAction.INSTANCE, request, listener); } @Override - public PutDecommissionRequestBuilder preparePutDecommission(PutDecommissionRequest request) { - return new PutDecommissionRequestBuilder(this, PutDecommissionAction.INSTANCE, request); + public DecommissionRequestBuilder prepareDecommission(DecommissionRequest request) { + return new DecommissionRequestBuilder(this, DecommissionAction.INSTANCE, request); } @Override diff --git a/server/src/main/java/org/opensearch/rest/action/admin/cluster/RestPutDecommissionAction.java b/server/src/main/java/org/opensearch/rest/action/admin/cluster/RestDecommissionAction.java similarity index 75% rename from server/src/main/java/org/opensearch/rest/action/admin/cluster/RestPutDecommissionAction.java rename to server/src/main/java/org/opensearch/rest/action/admin/cluster/RestDecommissionAction.java index 9155634c59239..3c76b255fe138 100644 --- a/server/src/main/java/org/opensearch/rest/action/admin/cluster/RestPutDecommissionAction.java +++ b/server/src/main/java/org/opensearch/rest/action/admin/cluster/RestDecommissionAction.java @@ -8,7 +8,7 @@ package org.opensearch.rest.action.admin.cluster; -import org.opensearch.action.admin.cluster.decommission.awareness.put.PutDecommissionRequest; +import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionRequest; import org.opensearch.client.Requests; import org.opensearch.client.node.NodeClient; import org.opensearch.cluster.decommission.DecommissionAttribute; @@ -28,7 +28,7 @@ * * @opensearch.api */ -public class RestPutDecommissionAction extends BaseRestHandler { +public class RestDecommissionAction extends BaseRestHandler { private static final TimeValue DEFAULT_TIMEOUT = TimeValue.timeValueSeconds(300L); @@ -44,14 +44,14 @@ public String getName() { @Override protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException { - PutDecommissionRequest putDecommissionRequest = createRequest(request); - return channel -> client.admin().cluster().putDecommission(putDecommissionRequest, new RestToXContentListener<>(channel)); + DecommissionRequest decommissionRequest = createRequest(request); + return channel -> client.admin().cluster().decommission(decommissionRequest, new RestToXContentListener<>(channel)); } - PutDecommissionRequest createRequest(RestRequest request) throws IOException { + DecommissionRequest createRequest(RestRequest request) throws IOException { String attributeName = null; String attributeValue = null; - PutDecommissionRequest putDecommissionRequest = Requests.putDecommissionRequest(); + DecommissionRequest decommissionRequest = Requests.decommissionRequest(); if (request.hasParam("awareness_attribute_name")) { attributeName = request.param("awareness_attribute_name"); } @@ -59,7 +59,7 @@ PutDecommissionRequest createRequest(RestRequest request) throws IOException { if (request.hasParam("awareness_attribute_value")) { attributeValue = request.param("awareness_attribute_value"); } - return putDecommissionRequest.setDecommissionAttribute(new DecommissionAttribute(attributeName, attributeValue)) + return decommissionRequest.setDecommissionAttribute(new DecommissionAttribute(attributeName, attributeValue)) .setTimeout(TimeValue.parseTimeValue(request.param("timeout"), DEFAULT_TIMEOUT, getClass().getSimpleName() + ".timeout")); } } diff --git a/server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequestTests.java b/server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/put/DecommissionRequestTests.java similarity index 78% rename from server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequestTests.java rename to server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/put/DecommissionRequestTests.java index 606158725c955..fe03fc42d4ba4 100644 --- a/server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionRequestTests.java +++ b/server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/put/DecommissionRequestTests.java @@ -15,16 +15,16 @@ import java.io.IOException; -public class PutDecommissionRequestTests extends OpenSearchTestCase { +public class DecommissionRequestTests extends OpenSearchTestCase { public void testSerialization() throws IOException { String attributeName = "zone"; String attributeValue = "zone-1"; DecommissionAttribute decommissionAttribute = new DecommissionAttribute(attributeName, attributeValue); TimeValue timeout = TimeValue.timeValueMillis(between(0, 30000)); - final PutDecommissionRequest originalRequest = new PutDecommissionRequest(decommissionAttribute, timeout); + final DecommissionRequest originalRequest = new DecommissionRequest(decommissionAttribute, timeout); - final PutDecommissionRequest deserialized = copyWriteable(originalRequest, writableRegistry(), PutDecommissionRequest::new); + final DecommissionRequest deserialized = copyWriteable(originalRequest, writableRegistry(), DecommissionRequest::new); assertEquals(deserialized.getDecommissionAttribute(), originalRequest.getDecommissionAttribute()); assertEquals(deserialized.getTimeout(), originalRequest.getTimeout()); @@ -37,7 +37,7 @@ public void testValidation() { DecommissionAttribute decommissionAttribute = new DecommissionAttribute(attributeName, attributeValue); TimeValue timeout = TimeValue.timeValueMillis(between(0, 30000)); - final PutDecommissionRequest request = new PutDecommissionRequest(decommissionAttribute, timeout); + final DecommissionRequest request = new DecommissionRequest(decommissionAttribute, timeout); ActionRequestValidationException e = request.validate(); assertNotNull(e); assertTrue(e.getMessage().contains("attribute name is missing")); @@ -48,7 +48,7 @@ public void testValidation() { DecommissionAttribute decommissionAttribute = new DecommissionAttribute(attributeName, attributeValue); TimeValue timeout = TimeValue.timeValueMillis(between(0, 30000)); - final PutDecommissionRequest request = new PutDecommissionRequest(decommissionAttribute, timeout); + final DecommissionRequest request = new DecommissionRequest(decommissionAttribute, timeout); ActionRequestValidationException e = request.validate(); assertNotNull(e); assertTrue(e.getMessage().contains("attribute value is missing")); @@ -59,7 +59,7 @@ public void testValidation() { DecommissionAttribute decommissionAttribute = new DecommissionAttribute(attributeName, attributeValue); TimeValue timeout = TimeValue.timeValueMillis(between(0, 30000)); - final PutDecommissionRequest request = new PutDecommissionRequest(decommissionAttribute, timeout); + final DecommissionRequest request = new DecommissionRequest(decommissionAttribute, timeout); ActionRequestValidationException e = request.validate(); assertNull(e); } diff --git a/server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionResponseTests.java b/server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/put/DecommissionResponseTests.java similarity index 66% rename from server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionResponseTests.java rename to server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/put/DecommissionResponseTests.java index 91f1cf3d9eb66..5ee5a5f3cf016 100644 --- a/server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/put/PutDecommissionResponseTests.java +++ b/server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/put/DecommissionResponseTests.java @@ -12,10 +12,10 @@ import java.io.IOException; -public class PutDecommissionResponseTests extends OpenSearchTestCase { +public class DecommissionResponseTests extends OpenSearchTestCase { public void testSerialization() throws IOException { - final PutDecommissionResponse originalRequest = new PutDecommissionResponse(true); - copyWriteable(originalRequest, writableRegistry(), PutDecommissionResponse::new); + final DecommissionResponse originalRequest = new DecommissionResponse(true); + copyWriteable(originalRequest, writableRegistry(), DecommissionResponse::new); // there are no fields so we're just checking that this doesn't throw anything } } diff --git a/server/src/test/java/org/opensearch/rest/action/admin/cluster/RestPutDecommissionActionTests.java b/server/src/test/java/org/opensearch/rest/action/admin/cluster/RestDecommissionActionTests.java similarity index 86% rename from server/src/test/java/org/opensearch/rest/action/admin/cluster/RestPutDecommissionActionTests.java rename to server/src/test/java/org/opensearch/rest/action/admin/cluster/RestDecommissionActionTests.java index 71be108e1637d..25b0ba0f273be 100644 --- a/server/src/test/java/org/opensearch/rest/action/admin/cluster/RestPutDecommissionActionTests.java +++ b/server/src/test/java/org/opensearch/rest/action/admin/cluster/RestDecommissionActionTests.java @@ -9,7 +9,7 @@ package org.opensearch.rest.action.admin.cluster; import org.junit.Before; -import org.opensearch.action.admin.cluster.decommission.awareness.put.PutDecommissionRequest; +import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionRequest; import org.opensearch.common.unit.TimeValue; import org.opensearch.rest.RestRequest; import org.opensearch.test.rest.FakeRestRequest; @@ -19,13 +19,13 @@ import java.util.HashMap; import java.util.Map; -public class RestPutDecommissionActionTests extends RestActionTestCase { +public class RestDecommissionActionTests extends RestActionTestCase { - private RestPutDecommissionAction action; + private RestDecommissionAction action; @Before public void setupAction() { - action = new RestPutDecommissionAction(); + action = new RestDecommissionAction(); controller().registerHandler(action); } @@ -37,7 +37,7 @@ public void testCreateRequest() throws IOException { RestRequest deprecatedRequest = buildRestRequest(params); - PutDecommissionRequest request = action.createRequest(deprecatedRequest); + DecommissionRequest request = action.createRequest(deprecatedRequest); assertEquals(request.getDecommissionAttribute().attributeName(), "zone"); assertEquals(request.getDecommissionAttribute().attributeValue(), "zone-1"); assertEquals(request.getTimeout(), TimeValue.timeValueSeconds(10L)); @@ -51,7 +51,7 @@ public void testCreateRequestWithDefaultTimeout() throws IOException { RestRequest deprecatedRequest = buildRestRequest(params); - PutDecommissionRequest request = action.createRequest(deprecatedRequest); + DecommissionRequest request = action.createRequest(deprecatedRequest); assertEquals(request.getDecommissionAttribute().attributeName(), "zone"); assertEquals(request.getDecommissionAttribute().attributeValue(), "zone-1"); assertEquals(request.getTimeout(), TimeValue.timeValueSeconds(300L)); From 62a3438d83d023d731d1a7d5bb9a5142d320daef Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Fri, 2 Sep 2022 16:35:58 +0530 Subject: [PATCH 094/187] Refactor GetDecommissionAction to GetDecommissionStateAction Signed-off-by: Rishab Nahata --- .../org/opensearch/action/ActionModule.java | 10 ++++---- ...n.java => GetDecommissionStateAction.java} | 8 +++---- ....java => GetDecommissionStateRequest.java} | 6 ++--- ...> GetDecommissionStateRequestBuilder.java} | 12 +++++----- ...java => GetDecommissionStateResponse.java} | 12 +++++----- ... TransportGetDecommissionStateAction.java} | 24 +++++++++---------- .../opensearch/client/ClusterAdminClient.java | 12 +++++----- .../java/org/opensearch/client/Requests.java | 6 ++--- .../client/support/AbstractClient.java | 20 ++++++++-------- .../admin/cluster/RestDecommissionAction.java | 2 +- ...va => RestGetDecommissionStateAction.java} | 12 +++++----- ...=> GetDecommissionStateResponseTests.java} | 10 ++++---- 12 files changed, 67 insertions(+), 67 deletions(-) rename server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/{GetDecommissionAction.java => GetDecommissionStateAction.java} (62%) rename server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/{GetDecommissionRequest.java => GetDecommissionStateRequest.java} (79%) rename server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/{GetDecommissionRequestBuilder.java => GetDecommissionStateRequestBuilder.java} (60%) rename server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/{GetDecommissionResponse.java => GetDecommissionStateResponse.java} (91%) rename server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/{TransportGetDecommissionAction.java => TransportGetDecommissionStateAction.java} (71%) rename server/src/main/java/org/opensearch/rest/action/admin/cluster/{RestGetDecommissionAction.java => RestGetDecommissionStateAction.java} (76%) rename server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/get/{GetDecommissionResponseTests.java => GetDecommissionStateResponseTests.java} (70%) diff --git a/server/src/main/java/org/opensearch/action/ActionModule.java b/server/src/main/java/org/opensearch/action/ActionModule.java index fd389cb69cc0e..1d19875267678 100644 --- a/server/src/main/java/org/opensearch/action/ActionModule.java +++ b/server/src/main/java/org/opensearch/action/ActionModule.java @@ -40,8 +40,8 @@ import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsAction; import org.opensearch.action.admin.cluster.configuration.TransportAddVotingConfigExclusionsAction; import org.opensearch.action.admin.cluster.configuration.TransportClearVotingConfigExclusionsAction; -import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionAction; -import org.opensearch.action.admin.cluster.decommission.awareness.get.TransportGetDecommissionAction; +import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionStateAction; +import org.opensearch.action.admin.cluster.decommission.awareness.get.TransportGetDecommissionStateAction; import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionAction; import org.opensearch.action.admin.cluster.decommission.awareness.put.TransportDecommissionAction; import org.opensearch.action.admin.cluster.health.ClusterHealthAction; @@ -309,7 +309,7 @@ import org.opensearch.rest.action.admin.cluster.RestDeleteRepositoryAction; import org.opensearch.rest.action.admin.cluster.RestDeleteSnapshotAction; import org.opensearch.rest.action.admin.cluster.RestDeleteStoredScriptAction; -import org.opensearch.rest.action.admin.cluster.RestGetDecommissionAction; +import org.opensearch.rest.action.admin.cluster.RestGetDecommissionStateAction; import org.opensearch.rest.action.admin.cluster.RestGetRepositoriesAction; import org.opensearch.rest.action.admin.cluster.RestGetScriptContextAction; import org.opensearch.rest.action.admin.cluster.RestGetScriptLanguageAction; @@ -691,7 +691,7 @@ public void reg // Decommission actions actions.register(DecommissionAction.INSTANCE, TransportDecommissionAction.class); - actions.register(GetDecommissionAction.INSTANCE, TransportGetDecommissionAction.class); + actions.register(GetDecommissionStateAction.INSTANCE, TransportGetDecommissionStateAction.class); return unmodifiableMap(actions.getRegistry()); } @@ -884,7 +884,7 @@ public void initRestHandlers(Supplier nodesInCluster) { } registerHandler.accept(new RestCatAction(catActions)); registerHandler.accept(new RestDecommissionAction()); - registerHandler.accept(new RestGetDecommissionAction()); + registerHandler.accept(new RestGetDecommissionStateAction()); // Remote Store APIs if (FeatureFlags.isEnabled(FeatureFlags.REMOTE_STORE)) { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionStateAction.java similarity index 62% rename from server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionAction.java rename to server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionStateAction.java index 72a7d5d53ce77..72fd1a26cb860 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionStateAction.java @@ -15,12 +15,12 @@ * * @opensearch.internal */ -public class GetDecommissionAction extends ActionType { +public class GetDecommissionStateAction extends ActionType { - public static final GetDecommissionAction INSTANCE = new GetDecommissionAction(); + public static final GetDecommissionStateAction INSTANCE = new GetDecommissionStateAction(); public static final String NAME = "cluster:admin/decommission/awareness/get"; - private GetDecommissionAction() { - super(NAME, GetDecommissionResponse::new); + private GetDecommissionStateAction() { + super(NAME, GetDecommissionStateResponse::new); } } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionStateRequest.java similarity index 79% rename from server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionRequest.java rename to server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionStateRequest.java index fa979544129c2..90150c71bf3f2 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionStateRequest.java @@ -20,11 +20,11 @@ * * @opensearch.internal */ -public class GetDecommissionRequest extends ClusterManagerNodeReadRequest { +public class GetDecommissionStateRequest extends ClusterManagerNodeReadRequest { - public GetDecommissionRequest() {} + public GetDecommissionStateRequest() {} - public GetDecommissionRequest(StreamInput in) throws IOException { + public GetDecommissionStateRequest(StreamInput in) throws IOException { super(in); } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionStateRequestBuilder.java similarity index 60% rename from server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionRequestBuilder.java rename to server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionStateRequestBuilder.java index c6a5925d87860..2b8616d0511cd 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionStateRequestBuilder.java @@ -16,15 +16,15 @@ * * @opensearch.internal */ -public class GetDecommissionRequestBuilder extends ClusterManagerNodeReadOperationRequestBuilder< - GetDecommissionRequest, - GetDecommissionResponse, - GetDecommissionRequestBuilder> { +public class GetDecommissionStateRequestBuilder extends ClusterManagerNodeReadOperationRequestBuilder< + GetDecommissionStateRequest, + GetDecommissionStateResponse, + GetDecommissionStateRequestBuilder> { /** * Creates new get decommissioned attributes request builder */ - public GetDecommissionRequestBuilder(OpenSearchClient client, GetDecommissionAction action) { - super(client, action, new GetDecommissionRequest()); + public GetDecommissionStateRequestBuilder(OpenSearchClient client, GetDecommissionStateAction action) { + super(client, action, new GetDecommissionStateRequest()); } } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionStateResponse.java similarity index 91% rename from server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponse.java rename to server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionStateResponse.java index 29c307065a700..4b6c1f20db008 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionStateResponse.java @@ -28,17 +28,17 @@ * * @opensearch.internal */ -public class GetDecommissionResponse extends ActionResponse implements ToXContentObject { +public class GetDecommissionStateResponse extends ActionResponse implements ToXContentObject { private final DecommissionAttribute decommissionedAttribute; private final DecommissionStatus status; - GetDecommissionResponse(DecommissionAttribute decommissionedAttribute, DecommissionStatus status) { + GetDecommissionStateResponse(DecommissionAttribute decommissionedAttribute, DecommissionStatus status) { this.decommissionedAttribute = decommissionedAttribute; this.status = status; } - GetDecommissionResponse(StreamInput in) throws IOException { + GetDecommissionStateResponse(StreamInput in) throws IOException { this.decommissionedAttribute = new DecommissionAttribute(in); this.status = DecommissionStatus.fromValue(in.readByte()); } @@ -72,7 +72,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws return builder; } - public static GetDecommissionResponse fromXContent(XContentParser parser) throws IOException { + public static GetDecommissionStateResponse fromXContent(XContentParser parser) throws IOException { ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser); String attributeType = "awareness"; XContentParser.Token token; @@ -125,14 +125,14 @@ public static GetDecommissionResponse fromXContent(XContentParser parser) throws } } } - return new GetDecommissionResponse(decommissionAttribute, status); + return new GetDecommissionStateResponse(decommissionAttribute, status); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - GetDecommissionResponse that = (GetDecommissionResponse) o; + GetDecommissionStateResponse that = (GetDecommissionStateResponse) o; return decommissionedAttribute.equals(that.decommissionedAttribute) && status == that.status; } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/TransportGetDecommissionAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/TransportGetDecommissionStateAction.java similarity index 71% rename from server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/TransportGetDecommissionAction.java rename to server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/TransportGetDecommissionStateAction.java index 28d91fd454d20..0080c6dfd41cd 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/TransportGetDecommissionAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/TransportGetDecommissionStateAction.java @@ -31,10 +31,10 @@ * * @opensearch.internal */ -public class TransportGetDecommissionAction extends TransportClusterManagerNodeReadAction { +public class TransportGetDecommissionStateAction extends TransportClusterManagerNodeReadAction { @Inject - public TransportGetDecommissionAction( + public TransportGetDecommissionStateAction( TransportService transportService, ClusterService clusterService, ThreadPool threadPool, @@ -42,12 +42,12 @@ public TransportGetDecommissionAction( IndexNameExpressionResolver indexNameExpressionResolver ) { super( - GetDecommissionAction.NAME, + GetDecommissionStateAction.NAME, transportService, clusterService, threadPool, actionFilters, - GetDecommissionRequest::new, + GetDecommissionStateRequest::new, indexNameExpressionResolver ); } @@ -58,34 +58,34 @@ protected String executor() { } @Override - protected GetDecommissionResponse read(StreamInput in) throws IOException { - return new GetDecommissionResponse(in); + protected GetDecommissionStateResponse read(StreamInput in) throws IOException { + return new GetDecommissionStateResponse(in); } @Override protected void clusterManagerOperation( - GetDecommissionRequest request, + GetDecommissionStateRequest request, ClusterState state, - ActionListener listener + ActionListener listener ) throws Exception { Metadata metadata = state.metadata(); // DecommissionAttributeMetadata decommissionedAttributes = metadata.custom(DecommissionAttributeMetadata.TYPE); // TODO - update once service layer changes are merged // <<<<<<< HEAD - listener.onResponse(new GetDecommissionResponse(new DecommissionAttribute("zone", "zone-1"), DecommissionStatus.DECOMMISSIONED)); + listener.onResponse(new GetDecommissionStateResponse(new DecommissionAttribute("zone", "zone-1"), DecommissionStatus.DECOMMISSIONED)); // ======= // if (decommissionedAttributes!=null) { - // listener.onResponse(new GetDecommissionResponse(decommissionedAttributes.decommissionAttribute(), + // listener.onResponse(new GetDecommissionStateResponse(decommissionedAttributes.decommissionAttribute(), // decommissionedAttributes.status())); // } // else { - // listener.onResponse(new GetDecommissionResponse()); + // listener.onResponse(new GetDecommissionStateResponse()); // } // >>>>>>> 1025b6e3e3e (Fix GET without PUT) } @Override - protected ClusterBlockException checkBlock(GetDecommissionRequest request, ClusterState state) { + protected ClusterBlockException checkBlock(GetDecommissionStateRequest request, ClusterState state) { return state.blocks().globalBlockedException(ClusterBlockLevel.METADATA_READ); } } diff --git a/server/src/main/java/org/opensearch/client/ClusterAdminClient.java b/server/src/main/java/org/opensearch/client/ClusterAdminClient.java index eca6b0bd8a5d9..69c63017632c1 100644 --- a/server/src/main/java/org/opensearch/client/ClusterAdminClient.java +++ b/server/src/main/java/org/opensearch/client/ClusterAdminClient.java @@ -37,9 +37,9 @@ import org.opensearch.action.admin.cluster.allocation.ClusterAllocationExplainRequest; import org.opensearch.action.admin.cluster.allocation.ClusterAllocationExplainRequestBuilder; import org.opensearch.action.admin.cluster.allocation.ClusterAllocationExplainResponse; -import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionRequest; -import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionRequestBuilder; -import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionResponse; +import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionStateRequest; +import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionStateRequestBuilder; +import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionStateResponse; import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionRequest; import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionRequestBuilder; import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionResponse; @@ -816,15 +816,15 @@ public interface ClusterAdminClient extends OpenSearchClient { /** * Get Decommissioned attribute */ - ActionFuture getDecommission(GetDecommissionRequest request); + ActionFuture getDecommission(GetDecommissionStateRequest request); /** * Get Decommissioned attribute */ - void getDecommission(GetDecommissionRequest request, ActionListener listener); + void getDecommission(GetDecommissionStateRequest request, ActionListener listener); /** * Get Decommissioned attribute */ - GetDecommissionRequestBuilder prepareGetDecommission(); + GetDecommissionStateRequestBuilder prepareGetDecommission(); } diff --git a/server/src/main/java/org/opensearch/client/Requests.java b/server/src/main/java/org/opensearch/client/Requests.java index a0eac23fc9d98..38674f8d917cd 100644 --- a/server/src/main/java/org/opensearch/client/Requests.java +++ b/server/src/main/java/org/opensearch/client/Requests.java @@ -32,7 +32,7 @@ package org.opensearch.client; -import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionRequest; +import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionStateRequest; import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionRequest; import org.opensearch.action.admin.cluster.health.ClusterHealthRequest; import org.opensearch.action.admin.cluster.node.info.NodesInfoRequest; @@ -565,7 +565,7 @@ public static DecommissionRequest decommissionRequest() { * * @return returns get decommission request */ - public static GetDecommissionRequest getDecommissionRequest() { - return new GetDecommissionRequest(); + public static GetDecommissionStateRequest getDecommissionStateRequest() { + return new GetDecommissionStateRequest(); } } diff --git a/server/src/main/java/org/opensearch/client/support/AbstractClient.java b/server/src/main/java/org/opensearch/client/support/AbstractClient.java index 00ec2f5086b12..253e20d84c410 100644 --- a/server/src/main/java/org/opensearch/client/support/AbstractClient.java +++ b/server/src/main/java/org/opensearch/client/support/AbstractClient.java @@ -43,10 +43,10 @@ import org.opensearch.action.admin.cluster.allocation.ClusterAllocationExplainRequest; import org.opensearch.action.admin.cluster.allocation.ClusterAllocationExplainRequestBuilder; import org.opensearch.action.admin.cluster.allocation.ClusterAllocationExplainResponse; -import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionAction; -import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionRequest; -import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionRequestBuilder; -import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionResponse; +import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionStateAction; +import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionStateRequest; +import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionStateRequestBuilder; +import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionStateResponse; import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionAction; import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionRequest; import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionRequestBuilder; @@ -1339,18 +1339,18 @@ public DecommissionRequestBuilder prepareDecommission(DecommissionRequest reques } @Override - public ActionFuture getDecommission(GetDecommissionRequest request) { - return execute(GetDecommissionAction.INSTANCE, request); + public ActionFuture getDecommission(GetDecommissionStateRequest request) { + return execute(GetDecommissionStateAction.INSTANCE, request); } @Override - public void getDecommission(GetDecommissionRequest request, ActionListener listener) { - execute(GetDecommissionAction.INSTANCE, request, listener); + public void getDecommission(GetDecommissionStateRequest request, ActionListener listener) { + execute(GetDecommissionStateAction.INSTANCE, request, listener); } @Override - public GetDecommissionRequestBuilder prepareGetDecommission() { - return new GetDecommissionRequestBuilder(this, GetDecommissionAction.INSTANCE); + public GetDecommissionStateRequestBuilder prepareGetDecommission() { + return new GetDecommissionStateRequestBuilder(this, GetDecommissionStateAction.INSTANCE); } } diff --git a/server/src/main/java/org/opensearch/rest/action/admin/cluster/RestDecommissionAction.java b/server/src/main/java/org/opensearch/rest/action/admin/cluster/RestDecommissionAction.java index 3c76b255fe138..f48b3a38f77e1 100644 --- a/server/src/main/java/org/opensearch/rest/action/admin/cluster/RestDecommissionAction.java +++ b/server/src/main/java/org/opensearch/rest/action/admin/cluster/RestDecommissionAction.java @@ -39,7 +39,7 @@ public List routes() { @Override public String getName() { - return "put_decommission_action"; + return "decommission_action"; } @Override diff --git a/server/src/main/java/org/opensearch/rest/action/admin/cluster/RestGetDecommissionAction.java b/server/src/main/java/org/opensearch/rest/action/admin/cluster/RestGetDecommissionStateAction.java similarity index 76% rename from server/src/main/java/org/opensearch/rest/action/admin/cluster/RestGetDecommissionAction.java rename to server/src/main/java/org/opensearch/rest/action/admin/cluster/RestGetDecommissionStateAction.java index 3f962e75c2274..2caca50501914 100644 --- a/server/src/main/java/org/opensearch/rest/action/admin/cluster/RestGetDecommissionAction.java +++ b/server/src/main/java/org/opensearch/rest/action/admin/cluster/RestGetDecommissionStateAction.java @@ -8,7 +8,7 @@ package org.opensearch.rest.action.admin.cluster; -import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionRequest; +import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionStateRequest; import org.opensearch.client.Requests; import org.opensearch.client.node.NodeClient; import org.opensearch.rest.BaseRestHandler; @@ -26,7 +26,7 @@ * * @opensearch.api */ -public class RestGetDecommissionAction extends BaseRestHandler { +public class RestGetDecommissionStateAction extends BaseRestHandler { @Override public List routes() { @@ -40,10 +40,10 @@ public String getName() { @Override public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException { - GetDecommissionRequest getDecommissionRequest = Requests.getDecommissionRequest(); - getDecommissionRequest.clusterManagerNodeTimeout( - request.paramAsTime("cluster_manager_timeout", getDecommissionRequest.clusterManagerNodeTimeout()) + GetDecommissionStateRequest getDecommissionStateRequest = Requests.getDecommissionStateRequest(); + getDecommissionStateRequest.clusterManagerNodeTimeout( + request.paramAsTime("cluster_manager_timeout", getDecommissionStateRequest.clusterManagerNodeTimeout()) ); - return channel -> client.admin().cluster().getDecommission(getDecommissionRequest, new RestToXContentListener<>(channel)); + return channel -> client.admin().cluster().getDecommission(getDecommissionStateRequest, new RestToXContentListener<>(channel)); } } diff --git a/server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponseTests.java b/server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionStateResponseTests.java similarity index 70% rename from server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponseTests.java rename to server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionStateResponseTests.java index be6d336128882..32f4fcf99f565 100644 --- a/server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionResponseTests.java +++ b/server/src/test/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionStateResponseTests.java @@ -15,19 +15,19 @@ import java.io.IOException; -public class GetDecommissionResponseTests extends AbstractXContentTestCase { +public class GetDecommissionStateResponseTests extends AbstractXContentTestCase { @Override - protected GetDecommissionResponse createTestInstance() { + protected GetDecommissionStateResponse createTestInstance() { DecommissionStatus status = DecommissionStatus.fromValue((byte) randomIntBetween(0, 5)); String attributeName = randomAlphaOfLength(10); String attributeValue = randomAlphaOfLength(10); DecommissionAttribute decommissionAttribute = new DecommissionAttribute(attributeName, attributeValue); - return new GetDecommissionResponse(decommissionAttribute, status); + return new GetDecommissionStateResponse(decommissionAttribute, status); } @Override - protected GetDecommissionResponse doParseInstance(XContentParser parser) throws IOException { - return GetDecommissionResponse.fromXContent(parser); + protected GetDecommissionStateResponse doParseInstance(XContentParser parser) throws IOException { + return GetDecommissionStateResponse.fromXContent(parser); } @Override From 162349e93f382a870431e520abb1658b4ad9d1f2 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Fri, 2 Sep 2022 16:44:04 +0530 Subject: [PATCH 095/187] Fix spotless check Signed-off-by: Rishab Nahata --- .../get/TransportGetDecommissionStateAction.java | 8 ++++++-- .../awareness/put/DecommissionRequestBuilder.java | 8 ++------ .../awareness/put/TransportDecommissionAction.java | 7 ++----- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/TransportGetDecommissionStateAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/TransportGetDecommissionStateAction.java index 0080c6dfd41cd..ab3567d23cbdb 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/TransportGetDecommissionStateAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/TransportGetDecommissionStateAction.java @@ -31,7 +31,9 @@ * * @opensearch.internal */ -public class TransportGetDecommissionStateAction extends TransportClusterManagerNodeReadAction { +public class TransportGetDecommissionStateAction extends TransportClusterManagerNodeReadAction< + GetDecommissionStateRequest, + GetDecommissionStateResponse> { @Inject public TransportGetDecommissionStateAction( @@ -72,7 +74,9 @@ protected void clusterManagerOperation( // DecommissionAttributeMetadata decommissionedAttributes = metadata.custom(DecommissionAttributeMetadata.TYPE); // TODO - update once service layer changes are merged // <<<<<<< HEAD - listener.onResponse(new GetDecommissionStateResponse(new DecommissionAttribute("zone", "zone-1"), DecommissionStatus.DECOMMISSIONED)); + listener.onResponse( + new GetDecommissionStateResponse(new DecommissionAttribute("zone", "zone-1"), DecommissionStatus.DECOMMISSIONED) + ); // ======= // if (decommissionedAttributes!=null) { // listener.onResponse(new GetDecommissionStateResponse(decommissionedAttributes.decommissionAttribute(), diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/DecommissionRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/DecommissionRequestBuilder.java index daeddf08b1684..2a9de91056785 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/DecommissionRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/DecommissionRequestBuilder.java @@ -20,15 +20,11 @@ * @opensearch.internal */ public class DecommissionRequestBuilder extends ClusterManagerNodeOperationRequestBuilder< - DecommissionRequest, + DecommissionRequest, DecommissionResponse, DecommissionRequestBuilder> { - public DecommissionRequestBuilder( - OpenSearchClient client, - ActionType action, - DecommissionRequest request - ) { + public DecommissionRequestBuilder(OpenSearchClient client, ActionType action, DecommissionRequest request) { super(client, action, request); } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/TransportDecommissionAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/TransportDecommissionAction.java index f07d562f36d93..166912c4a5e9c 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/TransportDecommissionAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/TransportDecommissionAction.java @@ -72,11 +72,8 @@ protected ClusterBlockException checkBlock(DecommissionRequest request, ClusterS } @Override - protected void clusterManagerOperation( - DecommissionRequest request, - ClusterState state, - ActionListener listener - ) throws Exception { + protected void clusterManagerOperation(DecommissionRequest request, ClusterState state, ActionListener listener) + throws Exception { logger.info("initiating awareness attribute [{}] decommissioning", request.getDecommissionAttribute().toString()); listener.onResponse(new DecommissionResponse(true)); // TODO - remove after integration // TODO - uncomment when integrating with the service From c885686b0fb12d9b0397d38c37518c65cbb466c5 Mon Sep 17 00:00:00 2001 From: Suraj Singh Date: Fri, 2 Sep 2022 11:52:02 -0700 Subject: [PATCH 096/187] [Segment Replication] Bump segment infos counter before commit during replica promotion (#4365) * [Segment Replication] Bump segment infos counter before commit during replica promotion Signed-off-by: Suraj Singh * Add changelog entry Signed-off-by: Suraj Singh Signed-off-by: Suraj Singh --- CHANGELOG.md | 1 + .../opensearch/index/engine/NRTReplicationEngine.java | 9 +++++++++ .../index/engine/NRTReplicationEngineTests.java | 2 ++ 3 files changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 76f134f10c29e..48d320dd5bce6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Add timeout on Mockito.verify to reduce flakyness in testReplicationOnDone test([#4314](https://github.com/opensearch-project/OpenSearch/pull/4314)) - Commit workflow for dependabot changelog helper ([#4331](https://github.com/opensearch-project/OpenSearch/pull/4331)) - Fixed cancellation of segment replication events ([#4225](https://github.com/opensearch-project/OpenSearch/pull/4225)) +- [Segment Replication] Bump segment infos counter before commit during replica promotion ([#4365](https://github.com/opensearch-project/OpenSearch/pull/4365)) - Bugs for dependabot changelog verifier workflow ([#4364](https://github.com/opensearch-project/OpenSearch/pull/4364)) - Fix flaky random test `NRTReplicationEngineTests.testUpdateSegments` ([#4352](https://github.com/opensearch-project/OpenSearch/pull/4352)) diff --git a/server/src/main/java/org/opensearch/index/engine/NRTReplicationEngine.java b/server/src/main/java/org/opensearch/index/engine/NRTReplicationEngine.java index 6f5b7030ed65f..cf753e3360c39 100644 --- a/server/src/main/java/org/opensearch/index/engine/NRTReplicationEngine.java +++ b/server/src/main/java/org/opensearch/index/engine/NRTReplicationEngine.java @@ -54,6 +54,8 @@ public class NRTReplicationEngine extends Engine { private final LocalCheckpointTracker localCheckpointTracker; private final WriteOnlyTranslogManager translogManager; + private static final int SI_COUNTER_INCREMENT = 10; + public NRTReplicationEngine(EngineConfig engineConfig) { super(engineConfig); store.incRef(); @@ -142,6 +144,13 @@ public synchronized void updateSegments(final SegmentInfos infos, long seqNo) th public void commitSegmentInfos() throws IOException { // TODO: This method should wait for replication events to finalize. final SegmentInfos latestSegmentInfos = getLatestSegmentInfos(); + /* + This is a workaround solution which decreases the chances of conflict on replica nodes when same file is copied + from two different primaries during failover. Increasing counter helps in avoiding this conflict as counter is + used to generate new segment file names. The ideal solution is to identify the counter from previous primary. + */ + latestSegmentInfos.counter = latestSegmentInfos.counter + SI_COUNTER_INCREMENT; + latestSegmentInfos.changed(); store.commitSegmentInfos(latestSegmentInfos, localCheckpointTracker.getMaxSeqNo(), localCheckpointTracker.getProcessedCheckpoint()); translogManager.syncTranslog(); } diff --git a/server/src/test/java/org/opensearch/index/engine/NRTReplicationEngineTests.java b/server/src/test/java/org/opensearch/index/engine/NRTReplicationEngineTests.java index 0008afcc901c7..540054782133a 100644 --- a/server/src/test/java/org/opensearch/index/engine/NRTReplicationEngineTests.java +++ b/server/src/test/java/org/opensearch/index/engine/NRTReplicationEngineTests.java @@ -252,6 +252,8 @@ public void testCommitSegmentInfos() throws Exception { // ensure getLatestSegmentInfos returns an updated infos ref with correct userdata. final SegmentInfos latestSegmentInfos = nrtEngine.getLatestSegmentInfos(); assertEquals(previousInfos.getGeneration(), latestSegmentInfos.getLastGeneration()); + assertEquals(previousInfos.getVersion(), latestSegmentInfos.getVersion()); + assertEquals(previousInfos.counter, latestSegmentInfos.counter); Map userData = latestSegmentInfos.getUserData(); assertEquals(processedCheckpoint, localCheckpointTracker.getProcessedCheckpoint()); assertEquals(maxSeqNo, Long.parseLong(userData.get(MAX_SEQ_NO))); From b206e98acb69c9d839b7eef74edff8f904eb4b88 Mon Sep 17 00:00:00 2001 From: Suraj Singh Date: Fri, 2 Sep 2022 11:52:35 -0700 Subject: [PATCH 097/187] [Segment Replication] Add check to cancel ongoing replication with old primary on onNewCheckpoint on replica (#4363) * [Segment Replication] Add check to cancel ongoing replication with old primary on onNewCheckpoint on replica Signed-off-by: Suraj Singh * Add changelog entry Signed-off-by: Suraj Singh * Address review comments Signed-off-by: Suraj Singh * Address review comments 2 Signed-off-by: Suraj Singh * Test failures Signed-off-by: Suraj Singh Signed-off-by: Suraj Singh --- CHANGELOG.md | 1 + .../replication/SegmentReplicationTarget.java | 4 ++ .../SegmentReplicationTargetService.java | 29 +++++++---- .../common/ReplicationCollection.java | 16 ++++-- .../SegmentReplicationTargetServiceTests.java | 49 ++++++++++++++++++- .../recovery/ReplicationCollectionTests.java | 18 +++++++ 6 files changed, 101 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 48d320dd5bce6..182a6b36fca48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - [Segment Replication] Bump segment infos counter before commit during replica promotion ([#4365](https://github.com/opensearch-project/OpenSearch/pull/4365)) - Bugs for dependabot changelog verifier workflow ([#4364](https://github.com/opensearch-project/OpenSearch/pull/4364)) - Fix flaky random test `NRTReplicationEngineTests.testUpdateSegments` ([#4352](https://github.com/opensearch-project/OpenSearch/pull/4352)) +- [Segment Replication] Add check to cancel ongoing replication with old primary on onNewCheckpoint on replica ([#4363](https://github.com/opensearch-project/OpenSearch/pull/4363)) ### Security - CVE-2022-25857 org.yaml:snakeyaml DOS vulnerability ([#4341](https://github.com/opensearch-project/OpenSearch/pull/4341)) diff --git a/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationTarget.java b/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationTarget.java index d1d6104a416ca..7c28406036ddd 100644 --- a/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationTarget.java +++ b/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationTarget.java @@ -56,6 +56,10 @@ public class SegmentReplicationTarget extends ReplicationTarget { private final SegmentReplicationState state; protected final MultiFileWriter multiFileWriter; + public ReplicationCheckpoint getCheckpoint() { + return this.checkpoint; + } + public SegmentReplicationTarget( ReplicationCheckpoint checkpoint, IndexShard indexShard, diff --git a/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationTargetService.java b/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationTargetService.java index 9e6b66dc4d7d6..8fc53ccd3bc08 100644 --- a/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationTargetService.java +++ b/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationTargetService.java @@ -18,6 +18,7 @@ import org.opensearch.common.Nullable; import org.opensearch.common.settings.Settings; import org.opensearch.common.util.CancellableThreads; +import org.opensearch.common.util.concurrent.ConcurrentCollections; import org.opensearch.index.shard.IndexEventListener; import org.opensearch.index.shard.IndexShard; import org.opensearch.index.shard.ShardId; @@ -34,7 +35,6 @@ import org.opensearch.transport.TransportRequestHandler; import org.opensearch.transport.TransportService; -import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; @@ -54,7 +54,7 @@ public class SegmentReplicationTargetService implements IndexEventListener { private final SegmentReplicationSourceFactory sourceFactory; - private final Map latestReceivedCheckpoint = new HashMap<>(); + private final Map latestReceivedCheckpoint = ConcurrentCollections.newConcurrentMap(); // Empty Implementation, only required while Segment Replication is under feature flag. public static final SegmentReplicationTargetService NO_OP = new SegmentReplicationTargetService() { @@ -151,14 +151,23 @@ public synchronized void onNewCheckpoint(final ReplicationCheckpoint receivedChe } else { latestReceivedCheckpoint.put(replicaShard.shardId(), receivedCheckpoint); } - if (onGoingReplications.isShardReplicating(replicaShard.shardId())) { - logger.trace( - () -> new ParameterizedMessage( - "Ignoring new replication checkpoint - shard is currently replicating to checkpoint {}", - replicaShard.getLatestReplicationCheckpoint() - ) - ); - return; + SegmentReplicationTarget ongoingReplicationTarget = onGoingReplications.getOngoingReplicationTarget(replicaShard.shardId()); + if (ongoingReplicationTarget != null) { + if (ongoingReplicationTarget.getCheckpoint().getPrimaryTerm() < receivedCheckpoint.getPrimaryTerm()) { + logger.trace( + "Cancelling ongoing replication from old primary with primary term {}", + ongoingReplicationTarget.getCheckpoint().getPrimaryTerm() + ); + onGoingReplications.cancel(ongoingReplicationTarget.getId(), "Cancelling stuck target after new primary"); + } else { + logger.trace( + () -> new ParameterizedMessage( + "Ignoring new replication checkpoint - shard is currently replicating to checkpoint {}", + replicaShard.getLatestReplicationCheckpoint() + ) + ); + return; + } } final Thread thread = Thread.currentThread(); if (replicaShard.shouldProcessCheckpoint(receivedCheckpoint)) { diff --git a/server/src/main/java/org/opensearch/indices/replication/common/ReplicationCollection.java b/server/src/main/java/org/opensearch/indices/replication/common/ReplicationCollection.java index d648ca6041ff8..20600856c9444 100644 --- a/server/src/main/java/org/opensearch/indices/replication/common/ReplicationCollection.java +++ b/server/src/main/java/org/opensearch/indices/replication/common/ReplicationCollection.java @@ -49,6 +49,7 @@ import java.util.Iterator; import java.util.List; import java.util.concurrent.ConcurrentMap; +import java.util.stream.Collectors; /** * This class holds a collection of all on going replication events on the current node (i.e., the node is the target node @@ -236,13 +237,18 @@ public boolean cancelForShard(ShardId shardId, String reason) { } /** - * check if a shard is currently replicating + * Get target for shard * - * @param shardId shardId for which to check if replicating - * @return true if shard is currently replicating + * @param shardId shardId + * @return ReplicationTarget for input shardId */ - public boolean isShardReplicating(ShardId shardId) { - return onGoingTargetEvents.values().stream().anyMatch(t -> t.indexShard.shardId().equals(shardId)); + public T getOngoingReplicationTarget(ShardId shardId) { + final List replicationTargetList = onGoingTargetEvents.values() + .stream() + .filter(t -> t.indexShard.shardId().equals(shardId)) + .collect(Collectors.toList()); + assert replicationTargetList.size() <= 1 : "More than one on-going replication targets"; + return replicationTargetList.size() > 0 ? replicationTargetList.get(0) : null; } /** diff --git a/server/src/test/java/org/opensearch/indices/replication/SegmentReplicationTargetServiceTests.java b/server/src/test/java/org/opensearch/indices/replication/SegmentReplicationTargetServiceTests.java index 7d9b0f09f21cd..1d253b0a9a300 100644 --- a/server/src/test/java/org/opensearch/indices/replication/SegmentReplicationTargetServiceTests.java +++ b/server/src/test/java/org/opensearch/indices/replication/SegmentReplicationTargetServiceTests.java @@ -49,6 +49,8 @@ public class SegmentReplicationTargetServiceTests extends IndexShardTestCase { private ReplicationCheckpoint initialCheckpoint; private ReplicationCheckpoint aheadCheckpoint; + private ReplicationCheckpoint newPrimaryCheckpoint; + @Override public void setUp() throws Exception { super.setUp(); @@ -74,6 +76,13 @@ public void setUp() throws Exception { initialCheckpoint.getSeqNo(), initialCheckpoint.getSegmentInfosVersion() + 1 ); + newPrimaryCheckpoint = new ReplicationCheckpoint( + initialCheckpoint.getShardId(), + initialCheckpoint.getPrimaryTerm() + 1, + initialCheckpoint.getSegmentsGen(), + initialCheckpoint.getSeqNo(), + initialCheckpoint.getSegmentInfosVersion() + 1 + ); } @Override @@ -160,7 +169,7 @@ public void testShardAlreadyReplicating() throws InterruptedException { // Create a spy of Target Service so that we can verify invocation of startReplication call with specific checkpoint on it. SegmentReplicationTargetService serviceSpy = spy(sut); final SegmentReplicationTarget target = new SegmentReplicationTarget( - checkpoint, + initialCheckpoint, replicaShard, replicationSource, mock(SegmentReplicationTargetService.SegmentReplicationListener.class) @@ -185,9 +194,47 @@ public void testShardAlreadyReplicating() throws InterruptedException { // wait for the new checkpoint to arrive, before the listener completes. latch.await(30, TimeUnit.SECONDS); + verify(targetSpy, times(0)).cancel(any()); verify(serviceSpy, times(0)).startReplication(eq(aheadCheckpoint), eq(replicaShard), any()); } + public void testOnNewCheckpointFromNewPrimaryCancelOngoingReplication() throws IOException, InterruptedException { + // Create a spy of Target Service so that we can verify invocation of startReplication call with specific checkpoint on it. + SegmentReplicationTargetService serviceSpy = spy(sut); + // Create a Mockito spy of target to stub response of few method calls. + final SegmentReplicationTarget targetSpy = spy( + new SegmentReplicationTarget( + initialCheckpoint, + replicaShard, + replicationSource, + mock(SegmentReplicationTargetService.SegmentReplicationListener.class) + ) + ); + + CountDownLatch latch = new CountDownLatch(1); + // Mocking response when startReplication is called on targetSpy we send a new checkpoint to serviceSpy and later reduce countdown + // of latch. + doAnswer(invocation -> { + final ActionListener listener = invocation.getArgument(0); + // a new checkpoint arrives before we've completed. + serviceSpy.onNewCheckpoint(newPrimaryCheckpoint, replicaShard); + listener.onResponse(null); + latch.countDown(); + return null; + }).when(targetSpy).startReplication(any()); + doNothing().when(targetSpy).onDone(); + + // start replication. This adds the target to on-ongoing replication collection + serviceSpy.startReplication(targetSpy); + + // wait for the new checkpoint to arrive, before the listener completes. + latch.await(5, TimeUnit.SECONDS); + doNothing().when(targetSpy).startReplication(any()); + verify(targetSpy, times(1)).cancel("Cancelling stuck target after new primary"); + verify(serviceSpy, times(1)).startReplication(eq(newPrimaryCheckpoint), eq(replicaShard), any()); + closeShards(replicaShard); + } + public void testNewCheckpointBehindCurrentCheckpoint() { SegmentReplicationTargetService spy = spy(sut); spy.onNewCheckpoint(checkpoint, replicaShard); diff --git a/server/src/test/java/org/opensearch/recovery/ReplicationCollectionTests.java b/server/src/test/java/org/opensearch/recovery/ReplicationCollectionTests.java index 7587f48503625..1789dd3b2a288 100644 --- a/server/src/test/java/org/opensearch/recovery/ReplicationCollectionTests.java +++ b/server/src/test/java/org/opensearch/recovery/ReplicationCollectionTests.java @@ -105,7 +105,25 @@ public void onFailure(ReplicationState state, OpenSearchException e, boolean sen collection.cancel(recoveryId, "meh"); } } + } + public void testMultiReplicationsForSingleShard() throws Exception { + try (ReplicationGroup shards = createGroup(0)) { + final ReplicationCollection collection = new ReplicationCollection<>(logger, threadPool); + final IndexShard shard1 = shards.addReplica(); + final IndexShard shard2 = shards.addReplica(); + final long recoveryId = startRecovery(collection, shards.getPrimaryNode(), shard1); + final long recoveryId2 = startRecovery(collection, shards.getPrimaryNode(), shard2); + try { + collection.getOngoingReplicationTarget(shard1.shardId()); + } catch (AssertionError e) { + assertEquals(e.getMessage(), "More than one on-going replication targets"); + } finally { + collection.cancel(recoveryId, "meh"); + collection.cancel(recoveryId2, "meh"); + } + closeShards(shard1, shard2); + } } public void testRecoveryCancellation() throws Exception { From 0c10674924d66547d009237f8dd333243aac6ac4 Mon Sep 17 00:00:00 2001 From: Kunal Kotwani Date: Fri, 2 Sep 2022 13:44:38 -0700 Subject: [PATCH 098/187] =?UTF-8?q?Adding=20support=20for=20labels=20on=20?= =?UTF-8?q?version=20bump=20PRs,=20skip=20label=20support=20for=E2=80=A6?= =?UTF-8?q?=20(#4391)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Adding support for labels on version bump PRs, skip label support for changelog verifier Signed-off-by: Kunal Kotwani * Add changelog Signed-off-by: Kunal Kotwani Signed-off-by: Kunal Kotwani --- .github/workflows/changelog_verifier.yml | 2 ++ .github/workflows/version.yml | 8 +++++++- CHANGELOG.md | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/changelog_verifier.yml b/.github/workflows/changelog_verifier.yml index cda5dde462068..96f99f17b016e 100644 --- a/.github/workflows/changelog_verifier.yml +++ b/.github/workflows/changelog_verifier.yml @@ -14,3 +14,5 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} - uses: dangoslen/changelog-enforcer@v3 + with: + skipLabels: "autocut" diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml index 030689642677a..42c2d21d106ce 100644 --- a/.github/workflows/version.yml +++ b/.github/workflows/version.yml @@ -5,7 +5,7 @@ on: tags: - '*.*.*' -jobs: +jobs: build: runs-on: ubuntu-latest steps: @@ -61,6 +61,8 @@ jobs: commit-message: Incremented version to ${{ env.NEXT_VERSION }} signoff: true delete-branch: true + labels: | + autocut title: '[AUTO] Incremented version to ${{ env.NEXT_VERSION }}.' body: | I've noticed that a new tag ${{ env.TAG }} was pushed, and incremented the version from ${{ env.CURRENT_VERSION }} to ${{ env.NEXT_VERSION }}. @@ -86,6 +88,8 @@ jobs: commit-message: Added bwc version ${{ env.NEXT_VERSION }} signoff: true delete-branch: true + labels: | + autocut title: '[AUTO] [${{ env.BASE_X }}] Added bwc version ${{ env.NEXT_VERSION }}.' body: | I've noticed that a new tag ${{ env.TAG }} was pushed, and added a bwc version ${{ env.NEXT_VERSION }}. @@ -111,6 +115,8 @@ jobs: commit-message: Added bwc version ${{ env.NEXT_VERSION }} signoff: true delete-branch: true + labels: | + autocut title: '[AUTO] [main] Added bwc version ${{ env.NEXT_VERSION }}.' body: | I've noticed that a new tag ${{ env.TAG }} was pushed, and added a bwc version ${{ env.NEXT_VERSION }}. diff --git a/CHANGELOG.md b/CHANGELOG.md index 182a6b36fca48..dc6c290253dbe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Label configuration for dependabot PRs ([#4348](https://github.com/opensearch-project/OpenSearch/pull/4348)) - Support for HTTP/2 (server-side) ([#3847](https://github.com/opensearch-project/OpenSearch/pull/3847)) - BWC version 2.2.2 ([#4383](https://github.com/opensearch-project/OpenSearch/pull/4383)) +- Support for labels on version bump PRs, skip label support for changelog verifier ([#4391](https://github.com/opensearch-project/OpenSearch/pull/4391)) ### Dependencies - Bumps `com.diffplug.spotless` from 6.9.1 to 6.10.0 From fab2a122cbb5ce535066d3c3b9c4ae9012d2e331 Mon Sep 17 00:00:00 2001 From: Suraj Singh Date: Fri, 2 Sep 2022 18:57:39 -0700 Subject: [PATCH 099/187] [Segment Replication] Extend FileChunkWriter to allow cancel on transport client (#4386) * [Segment Replication] Extend FileChunkWriter to allow cancel on retryable transport client Signed-off-by: Suraj Singh * Add changelog entry Signed-off-by: Suraj Singh * Address review comments Signed-off-by: Suraj Singh * Integration test Signed-off-by: Suraj Singh Signed-off-by: Suraj Singh --- CHANGELOG.md | 1 + .../replication/SegmentReplicationIT.java | 72 ++++++++++++++++++- .../indices/recovery/FileChunkWriter.java | 2 + .../OngoingSegmentReplications.java | 2 +- .../RemoteSegmentFileChunkWriter.java | 5 ++ .../SegmentReplicationSourceHandler.java | 6 +- .../SegmentReplicationSourceHandlerTests.java | 3 + 7 files changed, 88 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc6c290253dbe..0930923805d96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - [Segment Replication] Bump segment infos counter before commit during replica promotion ([#4365](https://github.com/opensearch-project/OpenSearch/pull/4365)) - Bugs for dependabot changelog verifier workflow ([#4364](https://github.com/opensearch-project/OpenSearch/pull/4364)) - Fix flaky random test `NRTReplicationEngineTests.testUpdateSegments` ([#4352](https://github.com/opensearch-project/OpenSearch/pull/4352)) +- [Segment Replication] Extend FileChunkWriter to allow cancel on transport client ([#4386](https://github.com/opensearch-project/OpenSearch/pull/4386)) - [Segment Replication] Add check to cancel ongoing replication with old primary on onNewCheckpoint on replica ([#4363](https://github.com/opensearch-project/OpenSearch/pull/4363)) ### Security diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/replication/SegmentReplicationIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/replication/SegmentReplicationIT.java index a9b6787d87bdf..16e9d78b17826 100644 --- a/server/src/internalClusterTest/java/org/opensearch/indices/replication/SegmentReplicationIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/indices/replication/SegmentReplicationIT.java @@ -33,17 +33,23 @@ import org.opensearch.index.engine.Segment; import org.opensearch.index.shard.IndexShard; import org.opensearch.indices.IndicesService; +import org.opensearch.indices.recovery.FileChunkRequest; import org.opensearch.indices.replication.common.ReplicationType; +import org.opensearch.plugins.Plugin; import org.opensearch.test.BackgroundIndexer; import org.opensearch.test.InternalTestCluster; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.transport.MockTransportService; +import org.opensearch.transport.TransportService; import java.io.IOException; +import java.util.Collection; import java.util.Arrays; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.Set; +import java.util.Optional; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.stream.Collectors; @@ -65,6 +71,11 @@ public static void assumeFeatureFlag() { assumeTrue("Segment replication Feature flag is enabled", Boolean.parseBoolean(System.getProperty(FeatureFlags.REPLICATION_TYPE))); } + @Override + protected Collection> nodePlugins() { + return Arrays.asList(MockTransportService.TestPlugin.class); + } + @Override public Settings indexSettings() { return Settings.builder() @@ -318,6 +329,65 @@ public void testReplicationAfterForceMerge() throws Exception { } } + public void testCancellation() throws Exception { + final String primaryNode = internalCluster().startNode(); + createIndex(INDEX_NAME, Settings.builder().put(indexSettings()).put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 1).build()); + ensureYellow(INDEX_NAME); + + final String replicaNode = internalCluster().startNode(); + + final SegmentReplicationSourceService segmentReplicationSourceService = internalCluster().getInstance( + SegmentReplicationSourceService.class, + primaryNode + ); + final IndexShard primaryShard = getIndexShard(primaryNode); + + CountDownLatch latch = new CountDownLatch(1); + + MockTransportService mockTransportService = ((MockTransportService) internalCluster().getInstance( + TransportService.class, + primaryNode + )); + mockTransportService.addSendBehavior( + internalCluster().getInstance(TransportService.class, replicaNode), + (connection, requestId, action, request, options) -> { + if (action.equals(SegmentReplicationTargetService.Actions.FILE_CHUNK)) { + FileChunkRequest req = (FileChunkRequest) request; + logger.debug("file chunk [{}] lastChunk: {}", req, req.lastChunk()); + if (req.name().endsWith("cfs") && req.lastChunk()) { + try { + latch.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + connection.sendRequest(requestId, action, request, options); + } + ); + + final int docCount = scaledRandomIntBetween(0, 200); + try ( + BackgroundIndexer indexer = new BackgroundIndexer( + INDEX_NAME, + "_doc", + client(), + -1, + RandomizedTest.scaledRandomIntBetween(2, 5), + false, + random() + ) + ) { + indexer.start(docCount); + waitForDocs(docCount, indexer); + + flush(INDEX_NAME); + } + segmentReplicationSourceService.beforeIndexShardClosed(primaryShard.shardId(), primaryShard, indexSettings()); + latch.countDown(); + assertDocCounts(docCount, primaryNode); + } + public void testStartReplicaAfterPrimaryIndexesDocs() throws Exception { final String primaryNode = internalCluster().startNode(); createIndex(INDEX_NAME, Settings.builder().put(indexSettings()).put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0).build()); diff --git a/server/src/main/java/org/opensearch/indices/recovery/FileChunkWriter.java b/server/src/main/java/org/opensearch/indices/recovery/FileChunkWriter.java index cb43af3b82e09..f1cc7b8dd1d89 100644 --- a/server/src/main/java/org/opensearch/indices/recovery/FileChunkWriter.java +++ b/server/src/main/java/org/opensearch/indices/recovery/FileChunkWriter.java @@ -28,4 +28,6 @@ void writeFileChunk( int totalTranslogOps, ActionListener listener ); + + default void cancel() {} } diff --git a/server/src/main/java/org/opensearch/indices/replication/OngoingSegmentReplications.java b/server/src/main/java/org/opensearch/indices/replication/OngoingSegmentReplications.java index 828aa29192fe3..1a97d334df58f 100644 --- a/server/src/main/java/org/opensearch/indices/replication/OngoingSegmentReplications.java +++ b/server/src/main/java/org/opensearch/indices/replication/OngoingSegmentReplications.java @@ -126,7 +126,7 @@ void startSegmentCopy(GetSegmentFilesRequest request, ActionListener Date: Sun, 4 Sep 2022 21:55:47 +0530 Subject: [PATCH 100/187] Added RestLayer Changes for PIT stats (#4217) Signed-off-by: Ajay Kumar Movva --- CHANGELOG.md | 1 + .../test/cat.shards/10_basic.yml | 3 +++ .../rest/action/cat/RestIndicesAction.java | 27 +++++++++++++++++++ .../rest/action/cat/RestNodesAction.java | 16 +++++++++++ .../rest/action/cat/RestShardsAction.java | 15 +++++++++++ .../action/cat/RestShardsActionTests.java | 4 +-- 6 files changed, 64 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0930923805d96..c5af055dca8a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### Added - Github workflow for changelog verification ([#4085](https://github.com/opensearch-project/OpenSearch/pull/4085)) - Label configuration for dependabot PRs ([#4348](https://github.com/opensearch-project/OpenSearch/pull/4348)) +- Added RestLayer Changes for PIT stats ([#4217](https://github.com/opensearch-project/OpenSearch/pull/4217)) ### Changed diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.shards/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.shards/10_basic.yml index aa4abc7a11eae..f07a06aba4388 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.shards/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.shards/10_basic.yml @@ -67,6 +67,9 @@ search.scroll_current .+ \n search.scroll_time .+ \n search.scroll_total .+ \n + search.point_in_time_current .+ \n + search.point_in_time_time .+ \n + search.point_in_time_total .+ \n segments.count .+ \n segments.memory .+ \n segments.index_writer_memory .+ \n diff --git a/server/src/main/java/org/opensearch/rest/action/cat/RestIndicesAction.java b/server/src/main/java/org/opensearch/rest/action/cat/RestIndicesAction.java index a8cdff5775478..f04d0ab712b39 100644 --- a/server/src/main/java/org/opensearch/rest/action/cat/RestIndicesAction.java +++ b/server/src/main/java/org/opensearch/rest/action/cat/RestIndicesAction.java @@ -597,6 +597,24 @@ protected Table getTableWithHeader(final RestRequest request) { ); table.addCell("pri.search.scroll_total", "default:false;text-align:right;desc:completed scroll contexts"); + table.addCell( + "search.point_in_time_current", + "sibling:pri;alias:scc,searchPointInTimeCurrent;default:false;text-align:right;desc:open point in time contexts" + ); + table.addCell("pri.search.point_in_time_current", "default:false;text-align:right;desc:open point in time contexts"); + + table.addCell( + "search.point_in_time_time", + "sibling:pri;alias:scti,searchPointInTimeTime;default:false;text-align:right;desc:time point in time contexts held open" + ); + table.addCell("pri.search.point_in_time_time", "default:false;text-align:right;desc:time point in time contexts held open"); + + table.addCell( + "search.point_in_time_total", + "sibling:pri;alias:scto,searchPointInTimeTotal;default:false;text-align:right;desc:completed point in time contexts" + ); + table.addCell("pri.search.point_in_time_total", "default:false;text-align:right;desc:completed point in time contexts"); + table.addCell("segments.count", "sibling:pri;alias:sc,segmentsCount;default:false;text-align:right;desc:number of segments"); table.addCell("pri.segments.count", "default:false;text-align:right;desc:number of segments"); @@ -878,6 +896,15 @@ Table buildTable( table.addCell(totalStats.getSearch() == null ? null : totalStats.getSearch().getTotal().getScrollCount()); table.addCell(primaryStats.getSearch() == null ? null : primaryStats.getSearch().getTotal().getScrollCount()); + table.addCell(totalStats.getSearch() == null ? null : totalStats.getSearch().getTotal().getPitCurrent()); + table.addCell(primaryStats.getSearch() == null ? null : primaryStats.getSearch().getTotal().getPitCurrent()); + + table.addCell(totalStats.getSearch() == null ? null : totalStats.getSearch().getTotal().getPitTime()); + table.addCell(primaryStats.getSearch() == null ? null : primaryStats.getSearch().getTotal().getPitTime()); + + table.addCell(totalStats.getSearch() == null ? null : totalStats.getSearch().getTotal().getPitCount()); + table.addCell(primaryStats.getSearch() == null ? null : primaryStats.getSearch().getTotal().getPitCount()); + table.addCell(totalStats.getSegments() == null ? null : totalStats.getSegments().getCount()); table.addCell(primaryStats.getSegments() == null ? null : primaryStats.getSegments().getCount()); diff --git a/server/src/main/java/org/opensearch/rest/action/cat/RestNodesAction.java b/server/src/main/java/org/opensearch/rest/action/cat/RestNodesAction.java index 8d3081bec48e9..6346e5d23cd34 100644 --- a/server/src/main/java/org/opensearch/rest/action/cat/RestNodesAction.java +++ b/server/src/main/java/org/opensearch/rest/action/cat/RestNodesAction.java @@ -310,6 +310,19 @@ protected Table getTableWithHeader(final RestRequest request) { ); table.addCell("search.scroll_total", "alias:scto,searchScrollTotal;default:false;text-align:right;desc:completed scroll contexts"); + table.addCell( + "search.point_in_time_current", + "alias:scc,searchPointInTimeCurrent;default:false;text-align:right;desc:open point in time contexts" + ); + table.addCell( + "search.point_in_time_time", + "alias:scti,searchPointInTimeTime;default:false;text-align:right;desc:time point in time contexts held open" + ); + table.addCell( + "search.point_in_time_total", + "alias:scto,searchPointInTimeTotal;default:false;text-align:right;desc:completed point in time contexts" + ); + table.addCell("segments.count", "alias:sc,segmentsCount;default:false;text-align:right;desc:number of segments"); table.addCell("segments.memory", "alias:sm,segmentsMemory;default:false;text-align:right;desc:memory used by segments"); table.addCell( @@ -519,6 +532,9 @@ Table buildTable( table.addCell(searchStats == null ? null : searchStats.getTotal().getScrollCurrent()); table.addCell(searchStats == null ? null : searchStats.getTotal().getScrollTime()); table.addCell(searchStats == null ? null : searchStats.getTotal().getScrollCount()); + table.addCell(searchStats == null ? null : searchStats.getTotal().getPitCurrent()); + table.addCell(searchStats == null ? null : searchStats.getTotal().getPitTime()); + table.addCell(searchStats == null ? null : searchStats.getTotal().getPitCount()); SegmentsStats segmentsStats = indicesStats == null ? null : indicesStats.getSegments(); table.addCell(segmentsStats == null ? null : segmentsStats.getCount()); diff --git a/server/src/main/java/org/opensearch/rest/action/cat/RestShardsAction.java b/server/src/main/java/org/opensearch/rest/action/cat/RestShardsAction.java index 6bf24951fe6c9..5cb5a7876669e 100644 --- a/server/src/main/java/org/opensearch/rest/action/cat/RestShardsAction.java +++ b/server/src/main/java/org/opensearch/rest/action/cat/RestShardsAction.java @@ -225,6 +225,18 @@ protected Table getTableWithHeader(final RestRequest request) { "alias:scti,searchScrollTime;default:false;text-align:right;desc:time scroll contexts held open" ); table.addCell("search.scroll_total", "alias:scto,searchScrollTotal;default:false;text-align:right;desc:completed scroll contexts"); + table.addCell( + "search.point_in_time_current", + "alias:spc,searchPointInTimeCurrent;default:false;text-align:right;desc:open point in time contexts" + ); + table.addCell( + "search.point_in_time_time", + "alias:spti,searchPointInTimeTime;default:false;text-align:right;desc:time point in time contexts held open" + ); + table.addCell( + "search.point_in_time_total", + "alias:spto,searchPointInTimeTotal;default:false;text-align:right;desc:completed point in time contexts" + ); table.addCell("segments.count", "alias:sc,segmentsCount;default:false;text-align:right;desc:number of segments"); table.addCell("segments.memory", "alias:sm,segmentsMemory;default:false;text-align:right;desc:memory used by segments"); @@ -390,6 +402,9 @@ Table buildTable(RestRequest request, ClusterStateResponse state, IndicesStatsRe table.addCell(getOrNull(commonStats, CommonStats::getSearch, i -> i.getTotal().getScrollCurrent())); table.addCell(getOrNull(commonStats, CommonStats::getSearch, i -> i.getTotal().getScrollTime())); table.addCell(getOrNull(commonStats, CommonStats::getSearch, i -> i.getTotal().getScrollCount())); + table.addCell(getOrNull(commonStats, CommonStats::getSearch, i -> i.getTotal().getPitCurrent())); + table.addCell(getOrNull(commonStats, CommonStats::getSearch, i -> i.getTotal().getPitTime())); + table.addCell(getOrNull(commonStats, CommonStats::getSearch, i -> i.getTotal().getPitCount())); table.addCell(getOrNull(commonStats, CommonStats::getSegments, SegmentsStats::getCount)); table.addCell(getOrNull(commonStats, CommonStats::getSegments, SegmentsStats::getZeroMemory)); diff --git a/server/src/test/java/org/opensearch/rest/action/cat/RestShardsActionTests.java b/server/src/test/java/org/opensearch/rest/action/cat/RestShardsActionTests.java index ed3aa19afa146..a8679a087216d 100644 --- a/server/src/test/java/org/opensearch/rest/action/cat/RestShardsActionTests.java +++ b/server/src/test/java/org/opensearch/rest/action/cat/RestShardsActionTests.java @@ -134,8 +134,8 @@ public void testBuildTable() { assertThat(row.get(3).value, equalTo(shardRouting.state())); assertThat(row.get(6).value, equalTo(localNode.getHostAddress())); assertThat(row.get(7).value, equalTo(localNode.getId())); - assertThat(row.get(69).value, equalTo(shardStats.getDataPath())); - assertThat(row.get(70).value, equalTo(shardStats.getStatePath())); + assertThat(row.get(72).value, equalTo(shardStats.getDataPath())); + assertThat(row.get(73).value, equalTo(shardStats.getStatePath())); } } } From 4ed09955e7013405f21a959a044f131d3bf224f1 Mon Sep 17 00:00:00 2001 From: Movva Ajaykumar Date: Tue, 6 Sep 2022 05:56:39 +0530 Subject: [PATCH 101/187] Modified cat shards test for pit stats (#4408) Signed-off-by: Ajay Kumar Movva --- CHANGELOG.md | 1 + .../test/cat.shards/10_basic.yml | 93 ++++++++++++++++++- 2 files changed, 92 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c5af055dca8a6..3c7757c7bd070 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Fix flaky random test `NRTReplicationEngineTests.testUpdateSegments` ([#4352](https://github.com/opensearch-project/OpenSearch/pull/4352)) - [Segment Replication] Extend FileChunkWriter to allow cancel on transport client ([#4386](https://github.com/opensearch-project/OpenSearch/pull/4386)) - [Segment Replication] Add check to cancel ongoing replication with old primary on onNewCheckpoint on replica ([#4363](https://github.com/opensearch-project/OpenSearch/pull/4363)) +- Fixed the `_cat/shards/10_basic.yml` test cases fix. ### Security - CVE-2022-25857 org.yaml:snakeyaml DOS vulnerability ([#4341](https://github.com/opensearch-project/OpenSearch/pull/4341)) diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.shards/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.shards/10_basic.yml index f07a06aba4388..6ebe273d552cc 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.shards/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.shards/10_basic.yml @@ -1,11 +1,14 @@ --- "Help": - skip: - version: " - 7.99.99" - reason: shard path stats were added in 8.0.0 + version: " - 2.9.99" + reason: point in time stats were added in 3.0.0 + features: node_selector - do: cat.shards: help: true + node_selector: + version: "3.0.0 - " - match: $body: | @@ -85,6 +88,92 @@ path.state .+ \n $/ --- +"Help before - 3.0.0": + - skip: + version: "3.0.0 - " + reason: point in time stats were added in 3.0.0 + features: node_selector + - do: + cat.shards: + help: true + node_selector: + version: " - 2.9.99" + + - match: + $body: | + /^ index .+ \n + shard .+ \n + prirep .+ \n + state .+ \n + docs .+ \n + store .+ \n + ip .+ \n + id .+ \n + node .+ \n + sync_id .+ \n + unassigned.reason .+ \n + unassigned.at .+ \n + unassigned.for .+ \n + unassigned.details .+ \n + recoverysource.type .+ \n + completion.size .+ \n + fielddata.memory_size .+ \n + fielddata.evictions .+ \n + query_cache.memory_size .+ \n + query_cache.evictions .+ \n + flush.total .+ \n + flush.total_time .+ \n + get.current .+ \n + get.time .+ \n + get.total .+ \n + get.exists_time .+ \n + get.exists_total .+ \n + get.missing_time .+ \n + get.missing_total .+ \n + indexing.delete_current .+ \n + indexing.delete_time .+ \n + indexing.delete_total .+ \n + indexing.index_current .+ \n + indexing.index_time .+ \n + indexing.index_total .+ \n + indexing.index_failed .+ \n + merges.current .+ \n + merges.current_docs .+ \n + merges.current_size .+ \n + merges.total .+ \n + merges.total_docs .+ \n + merges.total_size .+ \n + merges.total_time .+ \n + refresh.total .+ \n + refresh.time .+ \n + refresh.external_total .+ \n + refresh.external_time .+ \n + refresh.listeners .+ \n + search.fetch_current .+ \n + search.fetch_time .+ \n + search.fetch_total .+ \n + search.open_contexts .+ \n + search.query_current .+ \n + search.query_time .+ \n + search.query_total .+ \n + search.scroll_current .+ \n + search.scroll_time .+ \n + search.scroll_total .+ \n + segments.count .+ \n + segments.memory .+ \n + segments.index_writer_memory .+ \n + segments.version_map_memory .+ \n + segments.fixed_bitset_memory .+ \n + seq_no.max .+ \n + seq_no.local_checkpoint .+ \n + seq_no.global_checkpoint .+ \n + warmer.current .+ \n + warmer.total .+ \n + warmer.total_time .+ \n + path.data .+ \n + path.state .+ \n + $/ +--- "Test cat shards output": - do: From ff2e4bf86bc00ff62b45a0acc21b9790946b0dc7 Mon Sep 17 00:00:00 2001 From: Sachin Kale Date: Tue, 6 Sep 2022 17:27:58 +0530 Subject: [PATCH 102/187] [Remote Store] Add index specific setting for remote repository (#4253) * Add index specific setting for remote repository * Fix for failover incremental uploads Signed-off-by: Sachin Kale --- CHANGELOG.md | 1 + .../cluster/metadata/IndexMetadata.java | 58 +++++++++++++--- .../common/settings/IndexScopedSettings.java | 6 +- .../org/opensearch/index/IndexService.java | 2 +- .../org/opensearch/index/IndexSettings.java | 9 +++ .../shard/RemoteStoreRefreshListener.java | 7 ++ .../opensearch/index/IndexSettingsTests.java | 69 ++++++++++++++++++- 7 files changed, 139 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c7757c7bd070..4e033e1ffb2bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Use RemoteSegmentStoreDirectory instead of RemoteDirectory ([#4240](https://github.com/opensearch-project/OpenSearch/pull/4240)) - Plugin ZIP publication groupId value is configurable ([#4156](https://github.com/opensearch-project/OpenSearch/pull/4156)) - Update to Netty 4.1.80.Final ([#4359](https://github.com/opensearch-project/OpenSearch/pull/4359)) +- Add index specific setting for remote repository ([#4253](https://github.com/opensearch-project/OpenSearch/pull/4253)) ### Deprecated diff --git a/server/src/main/java/org/opensearch/cluster/metadata/IndexMetadata.java b/server/src/main/java/org/opensearch/cluster/metadata/IndexMetadata.java index 759891e88039b..cd1c92a8b109f 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/IndexMetadata.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/IndexMetadata.java @@ -285,6 +285,8 @@ public Iterator> settings() { public static final String SETTING_REMOTE_STORE_ENABLED = "index.remote_store.enabled"; + public static final String SETTING_REMOTE_STORE_REPOSITORY = "index.remote_store.repository"; + public static final String SETTING_REMOTE_TRANSLOG_STORE_ENABLED = "index.remote_store.translog.enabled"; /** * Used to specify if the index data should be persisted in the remote store. @@ -322,6 +324,50 @@ public Iterator> settings() { Property.Final ); + /** + * Used to specify remote store repository to use for this index. + */ + public static final Setting INDEX_REMOTE_STORE_REPOSITORY_SETTING = Setting.simpleString( + SETTING_REMOTE_STORE_REPOSITORY, + new Setting.Validator<>() { + + @Override + public void validate(final String value) {} + + @Override + public void validate(final String value, final Map, Object> settings) { + if (value == null || value.isEmpty()) { + throw new IllegalArgumentException( + "Setting " + INDEX_REMOTE_STORE_REPOSITORY_SETTING.getKey() + " should be provided with non-empty repository ID" + ); + } else { + validateRemoteStoreSettingEnabled(settings, INDEX_REMOTE_STORE_REPOSITORY_SETTING); + } + } + + @Override + public Iterator> settings() { + final List> settings = Collections.singletonList(INDEX_REMOTE_STORE_ENABLED_SETTING); + return settings.iterator(); + } + }, + Property.IndexScope, + Property.Final + ); + + private static void validateRemoteStoreSettingEnabled(final Map, Object> settings, Setting setting) { + final Boolean isRemoteSegmentStoreEnabled = (Boolean) settings.get(INDEX_REMOTE_STORE_ENABLED_SETTING); + if (isRemoteSegmentStoreEnabled == false) { + throw new IllegalArgumentException( + "Settings " + + setting.getKey() + + " can ont be set/enabled when " + + INDEX_REMOTE_STORE_ENABLED_SETTING.getKey() + + " is set to true" + ); + } + } + /** * Used to specify if the index translog operations should be persisted in the remote store. */ @@ -335,16 +381,8 @@ public void validate(final Boolean value) {} @Override public void validate(final Boolean value, final Map, Object> settings) { - final Boolean isRemoteSegmentStoreEnabled = (Boolean) settings.get(INDEX_REMOTE_STORE_ENABLED_SETTING); - if (isRemoteSegmentStoreEnabled == false && value == true) { - throw new IllegalArgumentException( - "Settings " - + INDEX_REMOTE_TRANSLOG_STORE_ENABLED_SETTING.getKey() - + " cannot be enabled when " - + INDEX_REMOTE_STORE_ENABLED_SETTING.getKey() - + " is set to " - + settings.get(INDEX_REMOTE_STORE_ENABLED_SETTING) - ); + if (value == true) { + validateRemoteStoreSettingEnabled(settings, INDEX_REMOTE_TRANSLOG_STORE_ENABLED_SETTING); } } diff --git a/server/src/main/java/org/opensearch/common/settings/IndexScopedSettings.java b/server/src/main/java/org/opensearch/common/settings/IndexScopedSettings.java index a3fa2c7ee3112..7be9adc786f24 100644 --- a/server/src/main/java/org/opensearch/common/settings/IndexScopedSettings.java +++ b/server/src/main/java/org/opensearch/common/settings/IndexScopedSettings.java @@ -223,7 +223,11 @@ public final class IndexScopedSettings extends AbstractScopedSettings { FeatureFlags.REPLICATION_TYPE, Collections.singletonList(IndexMetadata.INDEX_REPLICATION_TYPE_SETTING), FeatureFlags.REMOTE_STORE, - Arrays.asList(IndexMetadata.INDEX_REMOTE_STORE_ENABLED_SETTING, IndexMetadata.INDEX_REMOTE_TRANSLOG_STORE_ENABLED_SETTING) + Arrays.asList( + IndexMetadata.INDEX_REMOTE_STORE_ENABLED_SETTING, + IndexMetadata.INDEX_REMOTE_TRANSLOG_STORE_ENABLED_SETTING, + IndexMetadata.INDEX_REMOTE_STORE_REPOSITORY_SETTING + ) ); public static final IndexScopedSettings DEFAULT_SCOPED_SETTINGS = new IndexScopedSettings(Settings.EMPTY, BUILT_IN_INDEX_SETTINGS); diff --git a/server/src/main/java/org/opensearch/index/IndexService.java b/server/src/main/java/org/opensearch/index/IndexService.java index e1427df1c34ab..92f957633db84 100644 --- a/server/src/main/java/org/opensearch/index/IndexService.java +++ b/server/src/main/java/org/opensearch/index/IndexService.java @@ -511,7 +511,7 @@ public synchronized IndexShard createShard( Store remoteStore = null; if (this.indexSettings.isRemoteStoreEnabled()) { Directory remoteDirectory = remoteDirectoryFactory.newDirectory( - clusterService.state().metadata().clusterUUID(), + this.indexSettings.getRemoteStoreRepository(), this.indexSettings, path ); diff --git a/server/src/main/java/org/opensearch/index/IndexSettings.java b/server/src/main/java/org/opensearch/index/IndexSettings.java index 657cb1ee55cb9..9c7f4804755d4 100644 --- a/server/src/main/java/org/opensearch/index/IndexSettings.java +++ b/server/src/main/java/org/opensearch/index/IndexSettings.java @@ -560,6 +560,7 @@ public final class IndexSettings { private final ReplicationType replicationType; private final boolean isRemoteStoreEnabled; private final boolean isRemoteTranslogStoreEnabled; + private final String remoteStoreRepository; // volatile fields are updated via #updateIndexMetadata(IndexMetadata) under lock private volatile Settings settings; private volatile IndexMetadata indexMetadata; @@ -721,6 +722,7 @@ public IndexSettings(final IndexMetadata indexMetadata, final Settings nodeSetti replicationType = ReplicationType.parseString(settings.get(IndexMetadata.SETTING_REPLICATION_TYPE)); isRemoteStoreEnabled = settings.getAsBoolean(IndexMetadata.SETTING_REMOTE_STORE_ENABLED, false); isRemoteTranslogStoreEnabled = settings.getAsBoolean(IndexMetadata.SETTING_REMOTE_TRANSLOG_STORE_ENABLED, false); + remoteStoreRepository = settings.get(IndexMetadata.SETTING_REMOTE_STORE_REPOSITORY); this.searchThrottled = INDEX_SEARCH_THROTTLED.get(settings); this.queryStringLenient = QUERY_STRING_LENIENT_SETTING.get(settings); this.queryStringAnalyzeWildcard = QUERY_STRING_ANALYZE_WILDCARD.get(nodeSettings); @@ -979,6 +981,13 @@ public boolean isRemoteTranslogStoreEnabled() { return isRemoteTranslogStoreEnabled; } + /** + * Returns if remote store is enabled for this index. + */ + public String getRemoteStoreRepository() { + return remoteStoreRepository; + } + /** * Returns the node settings. The settings returned from {@link #getSettings()} are a merged version of the * index settings and the node settings where node settings are overwritten by index settings. diff --git a/server/src/main/java/org/opensearch/index/shard/RemoteStoreRefreshListener.java b/server/src/main/java/org/opensearch/index/shard/RemoteStoreRefreshListener.java index 0d32e8d56e4d2..a8ca9891d9743 100644 --- a/server/src/main/java/org/opensearch/index/shard/RemoteStoreRefreshListener.java +++ b/server/src/main/java/org/opensearch/index/shard/RemoteStoreRefreshListener.java @@ -59,6 +59,13 @@ public RemoteStoreRefreshListener(IndexShard indexShard) { .getDelegate()).getDelegate(); this.primaryTerm = indexShard.getOperationPrimaryTerm(); localSegmentChecksumMap = new HashMap<>(); + if (indexShard.shardRouting.primary()) { + try { + this.remoteDirectory.init(); + } catch (IOException e) { + logger.error("Exception while initialising RemoteSegmentStoreDirectory", e); + } + } } @Override diff --git a/server/src/test/java/org/opensearch/index/IndexSettingsTests.java b/server/src/test/java/org/opensearch/index/IndexSettingsTests.java index e02eac85beafb..de5ef8851ae80 100644 --- a/server/src/test/java/org/opensearch/index/IndexSettingsTests.java +++ b/server/src/test/java/org/opensearch/index/IndexSettingsTests.java @@ -851,7 +851,7 @@ public void testEnablingRemoteTranslogStoreFailsWhenRemoteSegmentDisabled() { () -> IndexMetadata.INDEX_REMOTE_TRANSLOG_STORE_ENABLED_SETTING.get(indexSettings) ); assertEquals( - "Settings index.remote_store.translog.enabled cannot be enabled when index.remote_store.enabled is set to false", + "Settings index.remote_store.translog.enabled can ont be set/enabled when index.remote_store.enabled is set to true", iae.getMessage() ); } @@ -876,4 +876,71 @@ public void testEnablingRemoteStoreFailsWhenReplicationTypeIsDefault() { ); assertEquals("To enable index.remote_store.enabled, index.replication.type should be set to SEGMENT", iae.getMessage()); } + + public void testRemoteRepositoryDefaultSetting() { + IndexMetadata metadata = newIndexMeta( + "index", + Settings.builder().put(IndexMetadata.SETTING_VERSION_CREATED, Version.CURRENT).build() + ); + IndexSettings settings = new IndexSettings(metadata, Settings.EMPTY); + assertNull(settings.getRemoteStoreRepository()); + } + + public void testRemoteRepositoryExplicitSetting() { + IndexMetadata metadata = newIndexMeta( + "index", + Settings.builder() + .put(IndexMetadata.SETTING_VERSION_CREATED, Version.CURRENT) + .put(IndexMetadata.SETTING_REMOTE_STORE_ENABLED, true) + .put(IndexMetadata.SETTING_REMOTE_STORE_REPOSITORY, "repo1") + .build() + ); + IndexSettings settings = new IndexSettings(metadata, Settings.EMPTY); + assertEquals("repo1", settings.getRemoteStoreRepository()); + } + + public void testUpdateRemoteRepositoryFails() { + Set> remoteStoreSettingSet = new HashSet<>(); + remoteStoreSettingSet.add(IndexMetadata.INDEX_REMOTE_STORE_REPOSITORY_SETTING); + IndexScopedSettings settings = new IndexScopedSettings(Settings.EMPTY, remoteStoreSettingSet); + IllegalArgumentException error = expectThrows( + IllegalArgumentException.class, + () -> settings.updateSettings( + Settings.builder().put("index.remote_store.repository", randomUnicodeOfLength(10)).build(), + Settings.builder(), + Settings.builder(), + "index" + ) + ); + assertEquals(error.getMessage(), "final index setting [index.remote_store.repository], not updateable"); + } + + public void testSetRemoteRepositoryFailsWhenRemoteStoreIsNotEnabled() { + Settings indexSettings = Settings.builder() + .put("index.replication.type", ReplicationType.SEGMENT) + .put("index.remote_store.enabled", false) + .put("index.remote_store.repository", "repo1") + .build(); + IllegalArgumentException iae = expectThrows( + IllegalArgumentException.class, + () -> IndexMetadata.INDEX_REMOTE_STORE_REPOSITORY_SETTING.get(indexSettings) + ); + assertEquals( + "Settings index.remote_store.repository can ont be set/enabled when index.remote_store.enabled is set to true", + iae.getMessage() + ); + } + + public void testSetRemoteRepositoryFailsWhenEmptyString() { + Settings indexSettings = Settings.builder() + .put("index.replication.type", ReplicationType.SEGMENT) + .put("index.remote_store.enabled", false) + .put("index.remote_store.repository", "") + .build(); + IllegalArgumentException iae = expectThrows( + IllegalArgumentException.class, + () -> IndexMetadata.INDEX_REMOTE_STORE_REPOSITORY_SETTING.get(indexSettings) + ); + assertEquals("Setting index.remote_store.repository should be provided with non-empty repository ID", iae.getMessage()); + } } From b0e1f6abe58f545b87d759afe5794b6eac0be3c1 Mon Sep 17 00:00:00 2001 From: Suraj Singh Date: Tue, 6 Sep 2022 09:02:12 -0700 Subject: [PATCH 103/187] [Semgnet Replication] Update flaky testOnNewCheckpointFromNewPrimaryCancelOngoingReplication unit test (#4414) * [Semgnet Replication] Update flaky testOnNewCheckpointFromNewPrimaryCancelOngoingReplication unit test Signed-off-by: Suraj Singh * Add changelog entry Signed-off-by: Suraj Singh * Update changelog entry Signed-off-by: Suraj Singh Signed-off-by: Suraj Singh --- CHANGELOG.md | 1 + .../replication/SegmentReplicationTarget.java | 2 +- .../SegmentReplicationTargetServiceTests.java | 20 +++++++++++-------- .../index/shard/IndexShardTestCase.java | 1 + 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e033e1ffb2bf..cb48b3aedeea5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Fix flaky random test `NRTReplicationEngineTests.testUpdateSegments` ([#4352](https://github.com/opensearch-project/OpenSearch/pull/4352)) - [Segment Replication] Extend FileChunkWriter to allow cancel on transport client ([#4386](https://github.com/opensearch-project/OpenSearch/pull/4386)) - [Segment Replication] Add check to cancel ongoing replication with old primary on onNewCheckpoint on replica ([#4363](https://github.com/opensearch-project/OpenSearch/pull/4363)) +- [Segment Replication] Update flaky testOnNewCheckpointFromNewPrimaryCancelOngoingReplication unit test ([#4414](https://github.com/opensearch-project/OpenSearch/pull/4414)) - Fixed the `_cat/shards/10_basic.yml` test cases fix. ### Security diff --git a/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationTarget.java b/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationTarget.java index 7c28406036ddd..6a9406aca13b9 100644 --- a/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationTarget.java +++ b/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationTarget.java @@ -160,9 +160,9 @@ public void startReplication(ActionListener listener) { final StepListener getFilesListener = new StepListener<>(); final StepListener finalizeListener = new StepListener<>(); + cancellableThreads.checkForCancel(); logger.trace("[shardId {}] Replica starting replication [id {}]", shardId().getId(), getId()); // Get list of files to copy from this checkpoint. - cancellableThreads.checkForCancel(); state.setStage(SegmentReplicationState.Stage.GET_CHECKPOINT_INFO); source.getCheckpointMetadata(getId(), checkpoint, checkpointInfoListener); diff --git a/server/src/test/java/org/opensearch/indices/replication/SegmentReplicationTargetServiceTests.java b/server/src/test/java/org/opensearch/indices/replication/SegmentReplicationTargetServiceTests.java index 1d253b0a9a300..f2eb635f24bbf 100644 --- a/server/src/test/java/org/opensearch/indices/replication/SegmentReplicationTargetServiceTests.java +++ b/server/src/test/java/org/opensearch/indices/replication/SegmentReplicationTargetServiceTests.java @@ -15,6 +15,7 @@ import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.CancellableThreads; import org.opensearch.index.engine.NRTReplicationEngineFactory; import org.opensearch.index.shard.IndexShard; import org.opensearch.index.shard.IndexShardTestCase; @@ -29,6 +30,7 @@ import java.util.concurrent.TimeUnit; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.mockito.Mockito.doAnswer; @@ -37,6 +39,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.eq; +import static org.opensearch.indices.replication.SegmentReplicationState.Stage.CANCELLED; public class SegmentReplicationTargetServiceTests extends IndexShardTestCase { @@ -215,24 +218,25 @@ public void testOnNewCheckpointFromNewPrimaryCancelOngoingReplication() throws I // Mocking response when startReplication is called on targetSpy we send a new checkpoint to serviceSpy and later reduce countdown // of latch. doAnswer(invocation -> { - final ActionListener listener = invocation.getArgument(0); + // short circuit loop on new checkpoint request + doReturn(null).when(serviceSpy).startReplication(eq(newPrimaryCheckpoint), eq(replicaShard), any()); // a new checkpoint arrives before we've completed. serviceSpy.onNewCheckpoint(newPrimaryCheckpoint, replicaShard); - listener.onResponse(null); - latch.countDown(); + try { + invocation.callRealMethod(); + } catch (CancellableThreads.ExecutionCancelledException e) { + latch.countDown(); + } return null; }).when(targetSpy).startReplication(any()); - doNothing().when(targetSpy).onDone(); // start replication. This adds the target to on-ongoing replication collection serviceSpy.startReplication(targetSpy); - + latch.await(); // wait for the new checkpoint to arrive, before the listener completes. - latch.await(5, TimeUnit.SECONDS); - doNothing().when(targetSpy).startReplication(any()); + assertEquals(CANCELLED, targetSpy.state().getStage()); verify(targetSpy, times(1)).cancel("Cancelling stuck target after new primary"); verify(serviceSpy, times(1)).startReplication(eq(newPrimaryCheckpoint), eq(replicaShard), any()); - closeShards(replicaShard); } public void testNewCheckpointBehindCurrentCheckpoint() { diff --git a/test/framework/src/main/java/org/opensearch/index/shard/IndexShardTestCase.java b/test/framework/src/main/java/org/opensearch/index/shard/IndexShardTestCase.java index 1b40cb4f2dfa3..0838a1fe87aa4 100644 --- a/test/framework/src/main/java/org/opensearch/index/shard/IndexShardTestCase.java +++ b/test/framework/src/main/java/org/opensearch/index/shard/IndexShardTestCase.java @@ -1207,6 +1207,7 @@ public void getCheckpointMetadata( copyState.getPendingDeleteFiles() ) ); + copyState.decRef(); } catch (IOException e) { logger.error("Unexpected error computing CopyState", e); Assert.fail("Failed to compute copyState"); From f97cb4b4bcf8ba15ed251b9ff97053a2f4f4619d Mon Sep 17 00:00:00 2001 From: Marc Handalian Date: Tue, 6 Sep 2022 10:06:24 -0700 Subject: [PATCH 104/187] Segment Replication - Fix NoSuchFileException errors caused when computing metadata snapshot on primary shards. (#4366) * Segment Replication - Fix NoSuchFileException errors caused when computing metadata snapshot on primary shards. This change fixes the errors that occur when computing metadata snapshots on primary shards from the latest in-memory SegmentInfos. The error occurs when a segments_N file that is referenced by the in-memory infos is deleted as part of a concurrent commit. The segments themselves are incref'd by IndexWriter.incRefDeleter but the commit file (Segments_N) is not. This change resolves this by ignoring the segments_N file when computing metadata for CopyState and only sending incref'd segment files to replicas. Signed-off-by: Marc Handalian * Fix spotless. Signed-off-by: Marc Handalian * Update StoreTests.testCleanupAndPreserveLatestCommitPoint to assert additional segments are deleted. Signed-off-by: Marc Handalian * Rename snapshot to metadataMap in CheckpointInfoResponse. Signed-off-by: Marc Handalian * Refactor segmentReplicationDiff method to compute off two maps instead of MetadataSnapshots. Signed-off-by: Marc Handalian * Fix spotless. Signed-off-by: Marc Handalian * Revert catchall in SegmentReplicationSourceService. Signed-off-by: Marc Handalian * Revert log lvl change. Signed-off-by: Marc Handalian * Fix SegmentReplicationTargetTests Signed-off-by: Marc Handalian * Cleanup unused logger. Signed-off-by: Marc Handalian Signed-off-by: Marc Handalian Co-authored-by: Suraj Singh --- CHANGELOG.md | 1 + .../replication/SegmentReplicationIT.java | 50 ++++++- .../org/opensearch/index/store/Store.java | 133 +++++++++--------- .../replication/CheckpointInfoResponse.java | 30 ++-- .../SegmentReplicationSourceService.java | 7 +- .../replication/SegmentReplicationTarget.java | 36 ++--- .../indices/replication/common/CopyState.java | 28 +--- .../SegmentReplicationIndexShardTests.java | 7 +- .../opensearch/index/store/StoreTests.java | 131 ++++++++++++++--- .../OngoingSegmentReplicationsTests.java | 28 ++-- .../SegmentReplicationSourceHandlerTests.java | 8 +- .../SegmentReplicationSourceServiceTests.java | 4 +- .../SegmentReplicationTargetServiceTests.java | 2 +- .../SegmentReplicationTargetTests.java | 48 ++----- .../replication/common/CopyStateTests.java | 10 +- .../index/shard/IndexShardTestCase.java | 8 +- 16 files changed, 301 insertions(+), 230 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb48b3aedeea5..a2b6528783a39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Fix flaky random test `NRTReplicationEngineTests.testUpdateSegments` ([#4352](https://github.com/opensearch-project/OpenSearch/pull/4352)) - [Segment Replication] Extend FileChunkWriter to allow cancel on transport client ([#4386](https://github.com/opensearch-project/OpenSearch/pull/4386)) - [Segment Replication] Add check to cancel ongoing replication with old primary on onNewCheckpoint on replica ([#4363](https://github.com/opensearch-project/OpenSearch/pull/4363)) +- Fix NoSuchFileExceptions with segment replication when computing primary metadata snapshots ([#4366](https://github.com/opensearch-project/OpenSearch/pull/4366)) - [Segment Replication] Update flaky testOnNewCheckpointFromNewPrimaryCancelOngoingReplication unit test ([#4414](https://github.com/opensearch-project/OpenSearch/pull/4414)) - Fixed the `_cat/shards/10_basic.yml` test cases fix. diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/replication/SegmentReplicationIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/replication/SegmentReplicationIT.java index 16e9d78b17826..9b2ab753832d3 100644 --- a/server/src/internalClusterTest/java/org/opensearch/indices/replication/SegmentReplicationIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/indices/replication/SegmentReplicationIT.java @@ -9,7 +9,6 @@ package org.opensearch.indices.replication; import com.carrotsearch.randomizedtesting.RandomizedTest; -import org.apache.lucene.index.SegmentInfos; import org.junit.BeforeClass; import org.opensearch.action.admin.indices.segments.IndexShardSegments; import org.opensearch.action.admin.indices.segments.IndicesSegmentResponse; @@ -586,13 +585,56 @@ private void assertSegmentStats(int numberOfReplicas) throws IOException { ClusterState state = client(internalCluster().getMasterName()).admin().cluster().prepareState().get().getState(); final DiscoveryNode replicaNode = state.nodes().resolveNode(replicaShardRouting.currentNodeId()); IndexShard indexShard = getIndexShard(replicaNode.getName()); - final String lastCommitSegmentsFileName = SegmentInfos.getLastCommitSegmentsFileName(indexShard.store().directory()); // calls to readCommit will fail if a valid commit point and all its segments are not in the store. - SegmentInfos.readCommit(indexShard.store().directory(), lastCommitSegmentsFileName); + indexShard.store().readLastCommittedSegmentsInfo(); } } } + public void testDropPrimaryDuringReplication() throws Exception { + final Settings settings = Settings.builder() + .put(indexSettings()) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 6) + .put(IndexMetadata.SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT) + .build(); + final String clusterManagerNode = internalCluster().startClusterManagerOnlyNode(); + final String primaryNode = internalCluster().startDataOnlyNode(Settings.EMPTY); + createIndex(INDEX_NAME, settings); + internalCluster().startDataOnlyNodes(6); + ensureGreen(INDEX_NAME); + + int initialDocCount = scaledRandomIntBetween(100, 200); + try ( + BackgroundIndexer indexer = new BackgroundIndexer( + INDEX_NAME, + "_doc", + client(), + -1, + RandomizedTest.scaledRandomIntBetween(2, 5), + false, + random() + ) + ) { + indexer.start(initialDocCount); + waitForDocs(initialDocCount, indexer); + refresh(INDEX_NAME); + // don't wait for replication to complete, stop the primary immediately. + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(primaryNode)); + ensureYellow(INDEX_NAME); + + // start another replica. + internalCluster().startDataOnlyNode(); + ensureGreen(INDEX_NAME); + + // index another doc and refresh - without this the new replica won't catch up. + client().prepareIndex(INDEX_NAME).setId("1").setSource("foo", "bar").get(); + + flushAndRefresh(INDEX_NAME); + waitForReplicaUpdate(); + assertSegmentStats(6); + } + } + /** * Waits until the replica is caught up to the latest primary segments gen. * @throws Exception if assertion fails @@ -611,10 +653,12 @@ private void waitForReplicaUpdate() throws Exception { final List replicaShardSegments = segmentListMap.get(false); // if we don't have any segments yet, proceed. final ShardSegments primaryShardSegments = primaryShardSegmentsList.stream().findFirst().get(); + logger.debug("Primary Segments: {}", primaryShardSegments.getSegments()); if (primaryShardSegments.getSegments().isEmpty() == false) { final Map latestPrimarySegments = getLatestSegments(primaryShardSegments); final Long latestPrimaryGen = latestPrimarySegments.values().stream().findFirst().map(Segment::getGeneration).get(); for (ShardSegments shardSegments : replicaShardSegments) { + logger.debug("Replica {} Segments: {}", shardSegments.getShardRouting(), shardSegments.getSegments()); final boolean isReplicaCaughtUpToPrimary = shardSegments.getSegments() .stream() .anyMatch(segment -> segment.getGeneration() == latestPrimaryGen); diff --git a/server/src/main/java/org/opensearch/index/store/Store.java b/server/src/main/java/org/opensearch/index/store/Store.java index 58598ab2d08f4..9122c950a6ab6 100644 --- a/server/src/main/java/org/opensearch/index/store/Store.java +++ b/server/src/main/java/org/opensearch/index/store/Store.java @@ -105,6 +105,7 @@ import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; @@ -122,6 +123,7 @@ import static java.util.Collections.emptyMap; import static java.util.Collections.unmodifiableMap; import static org.opensearch.index.seqno.SequenceNumbers.LOCAL_CHECKPOINT_KEY; +import static org.opensearch.index.store.Store.MetadataSnapshot.loadMetadata; /** * A Store provides plain access to files written by an opensearch index shard. Each shard @@ -334,6 +336,51 @@ public MetadataSnapshot getMetadata(SegmentInfos segmentInfos) throws IOExceptio return new MetadataSnapshot(segmentInfos, directory, logger); } + /** + * Segment Replication method - Fetch a map of StoreFileMetadata for segments, ignoring Segment_N files. + * @param segmentInfos {@link SegmentInfos} from which to compute metadata. + * @return {@link Map} map file name to {@link StoreFileMetadata}. + */ + public Map getSegmentMetadataMap(SegmentInfos segmentInfos) throws IOException { + assert indexSettings.isSegRepEnabled(); + return loadMetadata(segmentInfos, directory, logger, true).fileMetadata; + } + + /** + * Segment Replication method + * Returns a diff between the Maps of StoreFileMetadata that can be used for getting list of files to copy over to a replica for segment replication. The returned diff will hold a list of files that are: + *

    + *
  • identical: they exist in both maps and they can be considered the same ie. they don't need to be recovered
  • + *
  • different: they exist in both maps but their they are not identical
  • + *
  • missing: files that exist in the source but not in the target
  • + *
+ */ + public static RecoveryDiff segmentReplicationDiff(Map source, Map target) { + final List identical = new ArrayList<>(); + final List different = new ArrayList<>(); + final List missing = new ArrayList<>(); + for (StoreFileMetadata value : source.values()) { + if (value.name().startsWith(IndexFileNames.SEGMENTS)) { + continue; + } + if (target.containsKey(value.name()) == false) { + missing.add(value); + } else { + final StoreFileMetadata fileMetadata = target.get(value.name()); + if (fileMetadata.isSame(value)) { + identical.add(value); + } else { + different.add(value); + } + } + } + return new RecoveryDiff( + Collections.unmodifiableList(identical), + Collections.unmodifiableList(different), + Collections.unmodifiableList(missing) + ); + } + /** * Renames all the given files from the key of the map to the * value of the map. All successfully renamed files are removed from the map in-place. @@ -709,31 +756,34 @@ public void cleanupAndVerify(String reason, MetadataSnapshot sourceMetadata) thr } /** - * This method deletes every file in this store that is not contained in either the remote or local metadata snapshots. + * Segment Replication method - + * This method deletes every file in this store that is not referenced by the passed in SegmentInfos or + * part of the latest on-disk commit point. * This method is used for segment replication when the in memory SegmentInfos can be ahead of the on disk segment file. * In this case files from both snapshots must be preserved. Verification has been done that all files are present on disk. * @param reason the reason for this cleanup operation logged for each deleted file - * @param localSnapshot The local snapshot from in memory SegmentInfos. + * @param infos {@link SegmentInfos} Files from this infos will be preserved on disk if present. * @throws IllegalStateException if the latest snapshot in this store differs from the given one after the cleanup. */ - public void cleanupAndPreserveLatestCommitPoint(String reason, MetadataSnapshot localSnapshot) throws IOException { + public void cleanupAndPreserveLatestCommitPoint(String reason, SegmentInfos infos) throws IOException { + assert indexSettings.isSegRepEnabled(); // fetch a snapshot from the latest on disk Segments_N file. This can be behind // the passed in local in memory snapshot, so we want to ensure files it references are not removed. metadataLock.writeLock().lock(); try (Lock writeLock = directory.obtainLock(IndexWriter.WRITE_LOCK_NAME)) { - cleanupFiles(reason, localSnapshot, getMetadata(readLastCommittedSegmentsInfo())); + cleanupFiles(reason, getMetadata(readLastCommittedSegmentsInfo()), infos.files(true)); } finally { metadataLock.writeLock().unlock(); } } - private void cleanupFiles(String reason, MetadataSnapshot localSnapshot, @Nullable MetadataSnapshot additionalSnapshot) + private void cleanupFiles(String reason, MetadataSnapshot localSnapshot, @Nullable Collection additionalFiles) throws IOException { assert metadataLock.isWriteLockedByCurrentThread(); for (String existingFile : directory.listAll()) { if (Store.isAutogenerated(existingFile) || localSnapshot.contains(existingFile) - || (additionalSnapshot != null && additionalSnapshot.contains(existingFile))) { + || (additionalFiles != null && additionalFiles.contains(existingFile))) { // don't delete snapshot file, or the checksums file (note, this is extra protection since the Store won't delete // checksum) continue; @@ -825,17 +875,9 @@ public void commitSegmentInfos(SegmentInfos latestSegmentInfos, long maxSeqNo, l userData.put(SequenceNumbers.MAX_SEQ_NO, Long.toString(maxSeqNo)); latestSegmentInfos.setUserData(userData, true); latestSegmentInfos.commit(directory()); - - // similar to TrimUnsafeCommits, create a commit with an appending IW, this will delete old commits and ensure all files - // associated with the SegmentInfos.commit are fsynced. - final List existingCommits = DirectoryReader.listCommits(directory); - assert existingCommits.isEmpty() == false : "Expected at least one commit but none found"; - final IndexCommit lastIndexCommit = existingCommits.get(existingCommits.size() - 1); - assert latestSegmentInfos.getSegmentsFileName().equals(lastIndexCommit.getSegmentsFileName()); - try (IndexWriter writer = newAppendingIndexWriter(directory, lastIndexCommit)) { - writer.setLiveCommitData(lastIndexCommit.getUserData().entrySet()); - writer.commit(); - } + directory.sync(latestSegmentInfos.files(true)); + directory.syncMetaData(); + cleanupAndPreserveLatestCommitPoint("After commit", latestSegmentInfos); } finally { metadataLock.writeLock().unlock(); } @@ -1033,6 +1075,11 @@ static LoadedMetadata loadMetadata(IndexCommit commit, Directory directory, Logg } static LoadedMetadata loadMetadata(SegmentInfos segmentInfos, Directory directory, Logger logger) throws IOException { + return loadMetadata(segmentInfos, directory, logger, false); + } + + static LoadedMetadata loadMetadata(SegmentInfos segmentInfos, Directory directory, Logger logger, boolean ignoreSegmentsFile) + throws IOException { long numDocs = Lucene.getNumDocs(segmentInfos); Map commitUserDataBuilder = new HashMap<>(); commitUserDataBuilder.putAll(segmentInfos.getUserData()); @@ -1067,8 +1114,10 @@ static LoadedMetadata loadMetadata(SegmentInfos segmentInfos, Directory director if (maxVersion == null) { maxVersion = org.opensearch.Version.CURRENT.minimumIndexCompatibilityVersion().luceneVersion; } - final String segmentsFile = segmentInfos.getSegmentsFileName(); - checksumFromLuceneFile(directory, segmentsFile, builder, logger, maxVersion, true); + if (ignoreSegmentsFile == false) { + final String segmentsFile = segmentInfos.getSegmentsFileName(); + checksumFromLuceneFile(directory, segmentsFile, builder, logger, maxVersion, true); + } return new LoadedMetadata(unmodifiableMap(builder), unmodifiableMap(commitUserDataBuilder), numDocs); } @@ -1148,7 +1197,6 @@ public Map asMap() { * Helper method used to group store files according to segment and commit. * * @see MetadataSnapshot#recoveryDiff(MetadataSnapshot) - * @see MetadataSnapshot#segmentReplicationDiff(MetadataSnapshot) */ private Iterable> getGroupedFilesIterable() { final Map> perSegment = new HashMap<>(); @@ -1241,51 +1289,6 @@ public RecoveryDiff recoveryDiff(MetadataSnapshot recoveryTargetSnapshot) { return recoveryDiff; } - /** - * Segment Replication method - * Returns a diff between the two snapshots that can be used for getting list of files to copy over to a replica for segment replication. The given snapshot is treated as the - * target and this snapshot as the source. The returned diff will hold a list of files that are: - *
    - *
  • identical: they exist in both snapshots and they can be considered the same ie. they don't need to be recovered
  • - *
  • different: they exist in both snapshots but their they are not identical
  • - *
  • missing: files that exist in the source but not in the target
  • - *
- */ - public RecoveryDiff segmentReplicationDiff(MetadataSnapshot recoveryTargetSnapshot) { - final List identical = new ArrayList<>(); - final List different = new ArrayList<>(); - final List missing = new ArrayList<>(); - final ArrayList identicalFiles = new ArrayList<>(); - for (List segmentFiles : getGroupedFilesIterable()) { - identicalFiles.clear(); - boolean consistent = true; - for (StoreFileMetadata meta : segmentFiles) { - StoreFileMetadata storeFileMetadata = recoveryTargetSnapshot.get(meta.name()); - if (storeFileMetadata == null) { - // Do not consider missing files as inconsistent in SegRep as replicas may lag while primary updates - // documents and generate new files specific to a segment - missing.add(meta); - } else if (storeFileMetadata.isSame(meta) == false) { - consistent = false; - different.add(meta); - } else { - identicalFiles.add(meta); - } - } - if (consistent) { - identical.addAll(identicalFiles); - } else { - different.addAll(identicalFiles); - } - } - RecoveryDiff recoveryDiff = new RecoveryDiff( - Collections.unmodifiableList(identical), - Collections.unmodifiableList(different), - Collections.unmodifiableList(missing) - ); - return recoveryDiff; - } - /** * Returns the number of files in this snapshot */ diff --git a/server/src/main/java/org/opensearch/indices/replication/CheckpointInfoResponse.java b/server/src/main/java/org/opensearch/indices/replication/CheckpointInfoResponse.java index a73a3b54184da..48c2dfd30f589 100644 --- a/server/src/main/java/org/opensearch/indices/replication/CheckpointInfoResponse.java +++ b/server/src/main/java/org/opensearch/indices/replication/CheckpointInfoResponse.java @@ -10,13 +10,12 @@ import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.index.store.Store; import org.opensearch.index.store.StoreFileMetadata; import org.opensearch.indices.replication.checkpoint.ReplicationCheckpoint; import org.opensearch.transport.TransportResponse; import java.io.IOException; -import java.util.Set; +import java.util.Map; /** * Response returned from a {@link SegmentReplicationSource} that includes the file metadata, and SegmentInfos @@ -28,52 +27,41 @@ public class CheckpointInfoResponse extends TransportResponse { private final ReplicationCheckpoint checkpoint; - private final Store.MetadataSnapshot snapshot; + private final Map metadataMap; private final byte[] infosBytes; - // pendingDeleteFiles are segments that have been merged away in the latest in memory SegmentInfos - // but are still referenced by the latest commit point (Segments_N). - private final Set pendingDeleteFiles; public CheckpointInfoResponse( final ReplicationCheckpoint checkpoint, - final Store.MetadataSnapshot snapshot, - final byte[] infosBytes, - final Set additionalFiles + final Map metadataMap, + final byte[] infosBytes ) { this.checkpoint = checkpoint; - this.snapshot = snapshot; + this.metadataMap = metadataMap; this.infosBytes = infosBytes; - this.pendingDeleteFiles = additionalFiles; } public CheckpointInfoResponse(StreamInput in) throws IOException { this.checkpoint = new ReplicationCheckpoint(in); - this.snapshot = new Store.MetadataSnapshot(in); + this.metadataMap = in.readMap(StreamInput::readString, StoreFileMetadata::new); this.infosBytes = in.readByteArray(); - this.pendingDeleteFiles = in.readSet(StoreFileMetadata::new); } @Override public void writeTo(StreamOutput out) throws IOException { checkpoint.writeTo(out); - snapshot.writeTo(out); + out.writeMap(metadataMap, StreamOutput::writeString, (valueOut, fc) -> fc.writeTo(valueOut)); out.writeByteArray(infosBytes); - out.writeCollection(pendingDeleteFiles); } public ReplicationCheckpoint getCheckpoint() { return checkpoint; } - public Store.MetadataSnapshot getSnapshot() { - return snapshot; + public Map getMetadataMap() { + return metadataMap; } public byte[] getInfosBytes() { return infosBytes; } - - public Set getPendingDeleteFiles() { - return pendingDeleteFiles; - } } diff --git a/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationSourceService.java b/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationSourceService.java index db3f87201b774..91b8243440ac5 100644 --- a/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationSourceService.java +++ b/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationSourceService.java @@ -133,12 +133,7 @@ public void messageReceived(CheckpointInfoRequest request, TransportChannel chan ); final CopyState copyState = ongoingSegmentReplications.prepareForReplication(request, segmentSegmentFileChunkWriter); channel.sendResponse( - new CheckpointInfoResponse( - copyState.getCheckpoint(), - copyState.getMetadataSnapshot(), - copyState.getInfosBytes(), - copyState.getPendingDeleteFiles() - ) + new CheckpointInfoResponse(copyState.getCheckpoint(), copyState.getMetadataMap(), copyState.getInfosBytes()) ); timer.stop(); logger.trace( diff --git a/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationTarget.java b/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationTarget.java index 6a9406aca13b9..26bec2203c599 100644 --- a/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationTarget.java +++ b/server/src/main/java/org/opensearch/indices/replication/SegmentReplicationTarget.java @@ -23,6 +23,7 @@ import org.opensearch.action.StepListener; import org.opensearch.common.UUIDs; import org.opensearch.common.bytes.BytesReference; +import org.opensearch.common.concurrent.GatedCloseable; import org.opensearch.common.lucene.Lucene; import org.opensearch.common.util.CancellableThreads; import org.opensearch.index.shard.IndexShard; @@ -37,12 +38,9 @@ import java.io.IOException; import java.nio.ByteBuffer; -import java.util.ArrayList; import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; +import java.util.Collections; +import java.util.Map; /** * Represents the target of a replication event. @@ -178,9 +176,7 @@ private void getFiles(CheckpointInfoResponse checkpointInfo, StepListener filesToFetch = new ArrayList(diff.missing); - Set storeFiles = new HashSet<>(Arrays.asList(store.directory().listAll())); - final Set pendingDeleteFiles = checkpointInfo.getPendingDeleteFiles() - .stream() - .filter(f -> storeFiles.contains(f.name()) == false) - .collect(Collectors.toSet()); - - filesToFetch.addAll(pendingDeleteFiles); - logger.trace("Files to fetch {}", filesToFetch); - - for (StoreFileMetadata file : filesToFetch) { + for (StoreFileMetadata file : diff.missing) { state.getIndex().addFileDetail(file.name(), file.length(), false); } // always send a req even if not fetching files so the primary can clear the copyState for this shard. state.setStage(SegmentReplicationState.Stage.GET_FILES); cancellableThreads.checkForCancel(); - source.getSegmentFiles(getId(), checkpointInfo.getCheckpoint(), filesToFetch, store, getFilesListener); + source.getSegmentFiles(getId(), checkpointInfo.getCheckpoint(), diff.missing, store, getFilesListener); } private void finalizeReplication(CheckpointInfoResponse checkpointInfoResponse, ActionListener listener) { @@ -231,7 +217,7 @@ private void finalizeReplication(CheckpointInfoResponse checkpointInfoResponse, responseCheckpoint.getSegmentsGen() ); indexShard.finalizeReplication(infos, responseCheckpoint.getSeqNo()); - store.cleanupAndPreserveLatestCommitPoint("finalize - clean with in memory infos", store.getMetadata(infos)); + store.cleanupAndPreserveLatestCommitPoint("finalize - clean with in memory infos", infos); } catch (CorruptIndexException | IndexFormatTooNewException | IndexFormatTooOldException ex) { // this is a fatal exception at this stage. // this means we transferred files from the remote that have not be checksummed and they are @@ -280,11 +266,13 @@ private ChecksumIndexInput toIndexInput(byte[] input) { ); } - Store.MetadataSnapshot getMetadataSnapshot() throws IOException { + Map getMetadataMap() throws IOException { if (indexShard.getSegmentInfosSnapshot() == null) { - return Store.MetadataSnapshot.EMPTY; + return Collections.emptyMap(); + } + try (final GatedCloseable snapshot = indexShard.getSegmentInfosSnapshot()) { + return store.getSegmentMetadataMap(snapshot.get()); } - return store.getMetadata(indexShard.getSegmentInfosSnapshot().get()); } @Override diff --git a/server/src/main/java/org/opensearch/indices/replication/common/CopyState.java b/server/src/main/java/org/opensearch/indices/replication/common/CopyState.java index c0e0b4dee2b3f..1dd0886fd2f36 100644 --- a/server/src/main/java/org/opensearch/indices/replication/common/CopyState.java +++ b/server/src/main/java/org/opensearch/indices/replication/common/CopyState.java @@ -15,14 +15,12 @@ import org.opensearch.common.concurrent.GatedCloseable; import org.opensearch.common.util.concurrent.AbstractRefCounted; import org.opensearch.index.shard.IndexShard; -import org.opensearch.index.store.Store; import org.opensearch.index.store.StoreFileMetadata; import org.opensearch.indices.replication.checkpoint.ReplicationCheckpoint; import java.io.IOException; import java.io.UncheckedIOException; -import java.util.HashSet; -import java.util.Set; +import java.util.Map; /** * An Opensearch-specific version of Lucene's CopyState class that @@ -37,8 +35,7 @@ public class CopyState extends AbstractRefCounted { private final ReplicationCheckpoint requestedReplicationCheckpoint; /** Actual ReplicationCheckpoint returned by the shard */ private final ReplicationCheckpoint replicationCheckpoint; - private final Store.MetadataSnapshot metadataSnapshot; - private final HashSet pendingDeleteFiles; + private final Map metadataMap; private final byte[] infosBytes; private GatedCloseable commitRef; private final IndexShard shard; @@ -49,7 +46,7 @@ public CopyState(ReplicationCheckpoint requestedReplicationCheckpoint, IndexShar this.shard = shard; this.segmentInfosRef = shard.getSegmentInfosSnapshot(); SegmentInfos segmentInfos = this.segmentInfosRef.get(); - this.metadataSnapshot = shard.store().getMetadata(segmentInfos); + this.metadataMap = shard.store().getSegmentMetadataMap(segmentInfos); this.replicationCheckpoint = new ReplicationCheckpoint( shard.shardId(), shard.getOperationPrimaryTerm(), @@ -57,18 +54,7 @@ public CopyState(ReplicationCheckpoint requestedReplicationCheckpoint, IndexShar shard.getProcessedLocalCheckpoint(), segmentInfos.getVersion() ); - - // Send files that are merged away in the latest SegmentInfos but not in the latest on disk Segments_N. - // This ensures that the store on replicas is in sync with the store on primaries. this.commitRef = shard.acquireLastIndexCommit(false); - Store.MetadataSnapshot metadata = shard.store().getMetadata(this.commitRef.get()); - final Store.RecoveryDiff diff = metadata.recoveryDiff(this.metadataSnapshot); - this.pendingDeleteFiles = new HashSet<>(diff.missing); - if (this.pendingDeleteFiles.isEmpty()) { - // If there are no additional files we can release the last commit immediately. - this.commitRef.close(); - this.commitRef = null; - } ByteBuffersDataOutput buffer = new ByteBuffersDataOutput(); // resource description and name are not used, but resource description cannot be null @@ -95,18 +81,14 @@ public ReplicationCheckpoint getCheckpoint() { return replicationCheckpoint; } - public Store.MetadataSnapshot getMetadataSnapshot() { - return metadataSnapshot; + public Map getMetadataMap() { + return metadataMap; } public byte[] getInfosBytes() { return infosBytes; } - public Set getPendingDeleteFiles() { - return pendingDeleteFiles; - } - public IndexShard getShard() { return shard; } diff --git a/server/src/test/java/org/opensearch/index/shard/SegmentReplicationIndexShardTests.java b/server/src/test/java/org/opensearch/index/shard/SegmentReplicationIndexShardTests.java index 88a3bdad53d0c..3af882a8087ec 100644 --- a/server/src/test/java/org/opensearch/index/shard/SegmentReplicationIndexShardTests.java +++ b/server/src/test/java/org/opensearch/index/shard/SegmentReplicationIndexShardTests.java @@ -489,12 +489,7 @@ private void resolveCheckpointInfoResponseListener(ActionListener additionalSegments = new ArrayList<>(); + for (String file : store.directory().listAll()) { + if (commitMetadata.contains(file) == false) { + additionalSegments.add(file); + } + } + assertFalse(additionalSegments.isEmpty()); + + // clean up everything not in the latest commit point. + store.cleanupAndPreserveLatestCommitPoint("test", store.readLastCommittedSegmentsInfo()); + + // we want to ensure commitMetadata files are preserved after calling cleanup + for (String existingFile : store.directory().listAll()) { + assertTrue(commitMetadata.contains(existingFile)); + assertFalse(additionalSegments.contains(existingFile)); + } + deleteContent(store.directory()); + IOUtils.close(store); + } + + public void testGetSegmentMetadataMap() throws IOException { + final ShardId shardId = new ShardId("index", "_na_", 1); + Store store = new Store( + shardId, + SEGMENT_REPLICATION_INDEX_SETTINGS, + new NIOFSDirectory(createTempDir()), + new DummyShardLock(shardId) + ); + store.createEmpty(Version.LATEST); + final Map metadataSnapshot = store.getSegmentMetadataMap(store.readLastCommittedSegmentsInfo()); + // no docs indexed only _N file exists. + assertTrue(metadataSnapshot.isEmpty()); + + // commit some docs to create a commit point. + commitRandomDocs(store); + + final Map snapshotAfterCommit = store.getSegmentMetadataMap(store.readLastCommittedSegmentsInfo()); + assertFalse(snapshotAfterCommit.isEmpty()); + assertFalse(snapshotAfterCommit.keySet().stream().anyMatch((name) -> name.startsWith(IndexFileNames.SEGMENTS))); + store.close(); + } + + public void testSegmentReplicationDiff() { + final String segmentName = "_0.si"; + final StoreFileMetadata SEGMENT_FILE = new StoreFileMetadata(segmentName, 1L, "0", Version.LATEST); + // source has file target is missing. + Store.RecoveryDiff diff = Store.segmentReplicationDiff(Map.of(segmentName, SEGMENT_FILE), Collections.emptyMap()); + assertEquals(List.of(SEGMENT_FILE), diff.missing); + assertTrue(diff.different.isEmpty()); + assertTrue(diff.identical.isEmpty()); + + // target has file not on source. + diff = Store.segmentReplicationDiff(Collections.emptyMap(), Map.of(segmentName, SEGMENT_FILE)); + assertTrue(diff.missing.isEmpty()); + assertTrue(diff.different.isEmpty()); + assertTrue(diff.identical.isEmpty()); + + // source and target have identical file. + diff = Store.segmentReplicationDiff(Map.of(segmentName, SEGMENT_FILE), Map.of(segmentName, SEGMENT_FILE)); + assertTrue(diff.missing.isEmpty()); + assertTrue(diff.different.isEmpty()); + assertEquals(List.of(SEGMENT_FILE), diff.identical); + + // source has diff copy of same file as target. + StoreFileMetadata SOURCE_DIFF_FILE = new StoreFileMetadata(segmentName, 1L, "abc", Version.LATEST); + diff = Store.segmentReplicationDiff(Map.of(segmentName, SOURCE_DIFF_FILE), Map.of(segmentName, SEGMENT_FILE)); + assertTrue(diff.missing.isEmpty()); + assertEquals(List.of(SOURCE_DIFF_FILE), diff.different); + assertTrue(diff.identical.isEmpty()); + + // ignore _N files if included in source map. + final String segmentsFile = IndexFileNames.SEGMENTS.concat("_2"); + StoreFileMetadata SEGMENTS_FILE = new StoreFileMetadata(segmentsFile, 1L, "abc", Version.LATEST); + diff = Store.segmentReplicationDiff(Map.of(segmentsFile, SEGMENTS_FILE), Collections.emptyMap()); + assertTrue(diff.missing.isEmpty()); + assertTrue(diff.different.isEmpty()); + assertTrue(diff.identical.isEmpty()); + } + + private void commitRandomDocs(Store store) throws IOException { + IndexWriter writer = indexRandomDocs(store); + writer.commit(); + writer.close(); + } + + private IndexWriter indexRandomDocs(Store store) throws IOException { IndexWriterConfig indexWriterConfig = newIndexWriterConfig(random(), new MockAnalyzer(random())).setCodec( TestUtil.getDefaultCodec() ); + indexWriterConfig.setCommitOnClose(false); indexWriterConfig.setIndexDeletionPolicy(NoDeletionPolicy.INSTANCE); IndexWriter writer = new IndexWriter(store.directory(), indexWriterConfig); int docs = 1 + random().nextInt(100); @@ -1171,21 +1281,6 @@ public void testcleanupAndPreserveLatestCommitPoint() throws IOException { ); doc.add(new SortedDocValuesField("dv", new BytesRef(TestUtil.randomRealisticUnicodeString(random())))); writer.addDocument(doc); - writer.commit(); - writer.close(); - - Store.MetadataSnapshot commitMetadata = store.getMetadata(); - - Store.MetadataSnapshot refreshMetadata = Store.MetadataSnapshot.EMPTY; - - store.cleanupAndPreserveLatestCommitPoint("test", refreshMetadata); - - // we want to ensure commitMetadata files are preserved after calling cleanup - for (String existingFile : store.directory().listAll()) { - assert (commitMetadata.contains(existingFile) == true); - } - - deleteContent(store.directory()); - IOUtils.close(store); + return writer; } } diff --git a/server/src/test/java/org/opensearch/indices/replication/OngoingSegmentReplicationsTests.java b/server/src/test/java/org/opensearch/indices/replication/OngoingSegmentReplicationsTests.java index f49ee0471b5e8..bd3106454f49b 100644 --- a/server/src/test/java/org/opensearch/indices/replication/OngoingSegmentReplicationsTests.java +++ b/server/src/test/java/org/opensearch/indices/replication/OngoingSegmentReplicationsTests.java @@ -11,28 +11,30 @@ import org.junit.Assert; import org.opensearch.OpenSearchException; import org.opensearch.action.ActionListener; +import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Settings; import org.opensearch.common.util.CancellableThreads; import org.opensearch.common.xcontent.XContentType; import org.opensearch.index.IndexService; +import org.opensearch.index.engine.NRTReplicationEngineFactory; import org.opensearch.index.shard.IndexShard; import org.opensearch.index.shard.IndexShardTestCase; import org.opensearch.index.shard.ShardId; -import org.opensearch.index.store.StoreFileMetadata; import org.opensearch.indices.IndicesService; import org.opensearch.indices.recovery.FileChunkWriter; import org.opensearch.indices.recovery.RecoverySettings; import org.opensearch.indices.replication.checkpoint.ReplicationCheckpoint; import org.opensearch.indices.replication.common.CopyState; +import org.opensearch.indices.replication.common.ReplicationType; import org.opensearch.transport.TransportService; import java.io.IOException; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; -import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -55,15 +57,18 @@ public class OngoingSegmentReplicationsTests extends IndexShardTestCase { private GetSegmentFilesRequest getSegmentFilesRequest; - final Settings settings = Settings.builder().put("node.name", SegmentReplicationTargetServiceTests.class.getSimpleName()).build(); + final Settings settings = Settings.builder() + .put("node.name", SegmentReplicationTargetServiceTests.class.getSimpleName()) + .put(IndexMetadata.SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT) + .build(); final ClusterSettings clusterSettings = new ClusterSettings(settings, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS); final RecoverySettings recoverySettings = new RecoverySettings(settings, clusterSettings); @Override public void setUp() throws Exception { super.setUp(); - primary = newStartedShard(true); - replica = newShard(primary.shardId(), false); + primary = newStartedShard(true, settings); + replica = newShard(false, settings, new NRTReplicationEngineFactory()); recoverReplica(replica, primary, true); replicaDiscoveryNode = replica.recoveryState().getTargetNode(); primaryDiscoveryNode = replica.recoveryState().getSourceNode(); @@ -93,6 +98,8 @@ public void tearDown() throws Exception { } public void testPrepareAndSendSegments() throws IOException { + indexDoc(primary, "1", "{\"foo\" : \"baz\"}", XContentType.JSON, "foobar"); + primary.refresh("Test"); OngoingSegmentReplications replications = spy(new OngoingSegmentReplications(mockIndicesService, recoverySettings)); final CheckpointInfoRequest request = new CheckpointInfoRequest( 1L, @@ -112,17 +119,14 @@ public void testPrepareAndSendSegments() throws IOException { 1L, replica.routingEntry().allocationId().getId(), replicaDiscoveryNode, - new ArrayList<>(copyState.getMetadataSnapshot().asMap().values()), + new ArrayList<>(copyState.getMetadataMap().values()), testCheckpoint ); - final Collection expectedFiles = List.copyOf(primary.store().getMetadata().asMap().values()); replications.startSegmentCopy(getSegmentFilesRequest, new ActionListener<>() { @Override public void onResponse(GetSegmentFilesResponse getSegmentFilesResponse) { - assertEquals(1, getSegmentFilesResponse.files.size()); - assertEquals(1, expectedFiles.size()); - assertTrue(expectedFiles.stream().findFirst().get().isSame(getSegmentFilesResponse.files.get(0))); + assertEquals(copyState.getMetadataMap().size(), getSegmentFilesResponse.files.size()); assertEquals(0, copyState.refCount()); assertFalse(replications.isInCopyStateMap(request.getCheckpoint())); assertEquals(0, replications.size()); @@ -181,7 +185,7 @@ public void testCancelReplication_AfterSendFilesStarts() throws IOException, Int 1L, replica.routingEntry().allocationId().getId(), replicaDiscoveryNode, - new ArrayList<>(copyState.getMetadataSnapshot().asMap().values()), + new ArrayList<>(copyState.getMetadataMap().values()), testCheckpoint ); replications.startSegmentCopy(getSegmentFilesRequest, new ActionListener<>() { diff --git a/server/src/test/java/org/opensearch/indices/replication/SegmentReplicationSourceHandlerTests.java b/server/src/test/java/org/opensearch/indices/replication/SegmentReplicationSourceHandlerTests.java index 5f6ec7e505805..cde5cd980a91d 100644 --- a/server/src/test/java/org/opensearch/indices/replication/SegmentReplicationSourceHandlerTests.java +++ b/server/src/test/java/org/opensearch/indices/replication/SegmentReplicationSourceHandlerTests.java @@ -19,6 +19,7 @@ import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.common.settings.Settings; import org.opensearch.common.util.CancellableThreads; +import org.opensearch.common.xcontent.XContentType; import org.opensearch.index.shard.IndexShard; import org.opensearch.index.shard.IndexShardTestCase; import org.opensearch.index.store.StoreFileMetadata; @@ -76,7 +77,7 @@ public void testSendFiles() throws IOException { 1 ); - final List expectedFiles = List.copyOf(copyState.getMetadataSnapshot().asMap().values()); + final List expectedFiles = List.copyOf(copyState.getMetadataMap().values()); final GetSegmentFilesRequest getSegmentFilesRequest = new GetSegmentFilesRequest( 1L, @@ -137,6 +138,9 @@ public void onFailure(Exception e) { } public void testSendFileFails() throws IOException { + // index some docs on the primary so a segment is created. + indexDoc(primary, "1", "{\"foo\" : \"baz\"}", XContentType.JSON, "foobar"); + primary.refresh("Test"); chunkWriter = (fileMetadata, position, content, lastChunk, totalTranslogOps, listener) -> listener.onFailure( new OpenSearchException("Test") ); @@ -153,7 +157,7 @@ public void testSendFileFails() throws IOException { 1 ); - final List expectedFiles = List.copyOf(copyState.getMetadataSnapshot().asMap().values()); + final List expectedFiles = List.copyOf(copyState.getMetadataMap().values()); final GetSegmentFilesRequest getSegmentFilesRequest = new GetSegmentFilesRequest( 1L, diff --git a/server/src/test/java/org/opensearch/indices/replication/SegmentReplicationSourceServiceTests.java b/server/src/test/java/org/opensearch/indices/replication/SegmentReplicationSourceServiceTests.java index 4bfdd81d50a1e..6183f1e5d9dfb 100644 --- a/server/src/test/java/org/opensearch/indices/replication/SegmentReplicationSourceServiceTests.java +++ b/server/src/test/java/org/opensearch/indices/replication/SegmentReplicationSourceServiceTests.java @@ -121,9 +121,7 @@ public void testCheckpointInfo() { public void onResponse(CheckpointInfoResponse response) { assertEquals(testCheckpoint, response.getCheckpoint()); assertNotNull(response.getInfosBytes()); - // CopyStateTests sets up one pending delete file and one committed segments file - assertEquals(1, response.getPendingDeleteFiles().size()); - assertEquals(1, response.getSnapshot().size()); + assertEquals(1, response.getMetadataMap().size()); } @Override diff --git a/server/src/test/java/org/opensearch/indices/replication/SegmentReplicationTargetServiceTests.java b/server/src/test/java/org/opensearch/indices/replication/SegmentReplicationTargetServiceTests.java index f2eb635f24bbf..7437cb22e44d1 100644 --- a/server/src/test/java/org/opensearch/indices/replication/SegmentReplicationTargetServiceTests.java +++ b/server/src/test/java/org/opensearch/indices/replication/SegmentReplicationTargetServiceTests.java @@ -62,7 +62,7 @@ public void setUp() throws Exception { .put("node.name", SegmentReplicationTargetServiceTests.class.getSimpleName()) .build(); final ClusterSettings clusterSettings = new ClusterSettings(settings, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS); - primaryShard = newStartedShard(true); + primaryShard = newStartedShard(true, settings); replicaShard = newShard(false, settings, new NRTReplicationEngineFactory()); recoverReplica(replicaShard, primaryShard, true); checkpoint = new ReplicationCheckpoint(replicaShard.shardId(), 0L, 0L, 0L, 0L); diff --git a/server/src/test/java/org/opensearch/indices/replication/SegmentReplicationTargetTests.java b/server/src/test/java/org/opensearch/indices/replication/SegmentReplicationTargetTests.java index 11217a46b3c69..f8341573770a6 100644 --- a/server/src/test/java/org/opensearch/indices/replication/SegmentReplicationTargetTests.java +++ b/server/src/test/java/org/opensearch/indices/replication/SegmentReplicationTargetTests.java @@ -18,7 +18,6 @@ import org.apache.lucene.index.SegmentInfos; import org.apache.lucene.index.Term; import org.apache.lucene.index.IndexFormatTooNewException; -import org.apache.lucene.index.IndexFileNames; import org.apache.lucene.store.ByteBuffersDataOutput; import org.apache.lucene.store.ByteBuffersIndexOutput; import org.apache.lucene.store.Directory; @@ -51,7 +50,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.Random; import java.util.Arrays; @@ -71,26 +69,13 @@ public class SegmentReplicationTargetTests extends IndexShardTestCase { private ReplicationCheckpoint repCheckpoint; private ByteBuffersDataOutput buffer; - private static final StoreFileMetadata SEGMENTS_FILE = new StoreFileMetadata(IndexFileNames.SEGMENTS, 1L, "0", Version.LATEST); - private static final StoreFileMetadata SEGMENTS_FILE_DIFF = new StoreFileMetadata( - IndexFileNames.SEGMENTS, - 5L, - "different", - Version.LATEST - ); - private static final StoreFileMetadata PENDING_DELETE_FILE = new StoreFileMetadata("pendingDelete.del", 1L, "1", Version.LATEST); + private static final String SEGMENT_NAME = "_0.si"; + private static final StoreFileMetadata SEGMENT_FILE = new StoreFileMetadata(SEGMENT_NAME, 1L, "0", Version.LATEST); + private static final StoreFileMetadata SEGMENT_FILE_DIFF = new StoreFileMetadata(SEGMENT_NAME, 5L, "different", Version.LATEST); - private static final Store.MetadataSnapshot SI_SNAPSHOT = new Store.MetadataSnapshot( - Map.of(SEGMENTS_FILE.name(), SEGMENTS_FILE), - null, - 0 - ); + private static final Map SI_SNAPSHOT = Map.of(SEGMENT_FILE.name(), SEGMENT_FILE); - private static final Store.MetadataSnapshot SI_SNAPSHOT_DIFFERENT = new Store.MetadataSnapshot( - Map.of(SEGMENTS_FILE_DIFF.name(), SEGMENTS_FILE_DIFF), - null, - 0 - ); + private static final Map SI_SNAPSHOT_DIFFERENT = Map.of(SEGMENT_FILE_DIFF.name(), SEGMENT_FILE_DIFF); private static final IndexSettings INDEX_SETTINGS = IndexSettingsModule.newIndexSettings( "index", @@ -135,7 +120,7 @@ public void getCheckpointMetadata( ReplicationCheckpoint checkpoint, ActionListener listener ) { - listener.onResponse(new CheckpointInfoResponse(checkpoint, SI_SNAPSHOT, buffer.toArrayCopy(), Set.of(PENDING_DELETE_FILE))); + listener.onResponse(new CheckpointInfoResponse(checkpoint, SI_SNAPSHOT, buffer.toArrayCopy())); } @Override @@ -146,9 +131,8 @@ public void getSegmentFiles( Store store, ActionListener listener ) { - assertEquals(filesToFetch.size(), 2); - assert (filesToFetch.contains(SEGMENTS_FILE)); - assert (filesToFetch.contains(PENDING_DELETE_FILE)); + assertEquals(1, filesToFetch.size()); + assert (filesToFetch.contains(SEGMENT_FILE)); listener.onResponse(new GetSegmentFilesResponse(filesToFetch)); } }; @@ -230,7 +214,7 @@ public void getCheckpointMetadata( ReplicationCheckpoint checkpoint, ActionListener listener ) { - listener.onResponse(new CheckpointInfoResponse(checkpoint, SI_SNAPSHOT, buffer.toArrayCopy(), Set.of(PENDING_DELETE_FILE))); + listener.onResponse(new CheckpointInfoResponse(checkpoint, SI_SNAPSHOT, buffer.toArrayCopy())); } @Override @@ -273,7 +257,7 @@ public void getCheckpointMetadata( ReplicationCheckpoint checkpoint, ActionListener listener ) { - listener.onResponse(new CheckpointInfoResponse(checkpoint, SI_SNAPSHOT, buffer.toArrayCopy(), Set.of(PENDING_DELETE_FILE))); + listener.onResponse(new CheckpointInfoResponse(checkpoint, SI_SNAPSHOT, buffer.toArrayCopy())); } @Override @@ -318,7 +302,7 @@ public void getCheckpointMetadata( ReplicationCheckpoint checkpoint, ActionListener listener ) { - listener.onResponse(new CheckpointInfoResponse(checkpoint, SI_SNAPSHOT, buffer.toArrayCopy(), Set.of(PENDING_DELETE_FILE))); + listener.onResponse(new CheckpointInfoResponse(checkpoint, SI_SNAPSHOT, buffer.toArrayCopy())); } @Override @@ -362,7 +346,7 @@ public void getCheckpointMetadata( ReplicationCheckpoint checkpoint, ActionListener listener ) { - listener.onResponse(new CheckpointInfoResponse(checkpoint, SI_SNAPSHOT, buffer.toArrayCopy(), Set.of(PENDING_DELETE_FILE))); + listener.onResponse(new CheckpointInfoResponse(checkpoint, SI_SNAPSHOT, buffer.toArrayCopy())); } @Override @@ -380,7 +364,7 @@ public void getSegmentFiles( SegmentReplicationTargetService.SegmentReplicationListener.class ); segrepTarget = spy(new SegmentReplicationTarget(repCheckpoint, indexShard, segrepSource, segRepListener)); - when(segrepTarget.getMetadataSnapshot()).thenReturn(SI_SNAPSHOT_DIFFERENT); + when(segrepTarget.getMetadataMap()).thenReturn(SI_SNAPSHOT_DIFFERENT); segrepTarget.startReplication(new ActionListener() { @Override public void onResponse(Void replicationResponse) { @@ -413,9 +397,7 @@ public void getCheckpointMetadata( ReplicationCheckpoint checkpoint, ActionListener listener ) { - listener.onResponse( - new CheckpointInfoResponse(checkpoint, storeMetadataSnapshots.get(1), buffer.toArrayCopy(), Set.of(PENDING_DELETE_FILE)) - ); + listener.onResponse(new CheckpointInfoResponse(checkpoint, storeMetadataSnapshots.get(1).asMap(), buffer.toArrayCopy())); } @Override @@ -434,7 +416,7 @@ public void getSegmentFiles( ); segrepTarget = spy(new SegmentReplicationTarget(repCheckpoint, indexShard, segrepSource, segRepListener)); - when(segrepTarget.getMetadataSnapshot()).thenReturn(storeMetadataSnapshots.get(0)); + when(segrepTarget.getMetadataMap()).thenReturn(storeMetadataSnapshots.get(0).asMap()); segrepTarget.startReplication(new ActionListener() { @Override public void onResponse(Void replicationResponse) { diff --git a/server/src/test/java/org/opensearch/indices/replication/common/CopyStateTests.java b/server/src/test/java/org/opensearch/indices/replication/common/CopyStateTests.java index a6f0cf7e98411..77a4a6d22039e 100644 --- a/server/src/test/java/org/opensearch/indices/replication/common/CopyStateTests.java +++ b/server/src/test/java/org/opensearch/indices/replication/common/CopyStateTests.java @@ -22,7 +22,6 @@ import org.opensearch.indices.replication.checkpoint.ReplicationCheckpoint; import java.io.IOException; -import java.util.Set; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -32,6 +31,7 @@ public class CopyStateTests extends IndexShardTestCase { private static final long EXPECTED_LONG_VALUE = 1L; private static final ShardId TEST_SHARD_ID = new ShardId("testIndex", "testUUID", 0); private static final StoreFileMetadata SEGMENTS_FILE = new StoreFileMetadata(IndexFileNames.SEGMENTS, 1L, "0", Version.LATEST); + private static final StoreFileMetadata SEGMENT_FILE = new StoreFileMetadata("_0.si", 1L, "0", Version.LATEST); private static final StoreFileMetadata PENDING_DELETE_FILE = new StoreFileMetadata("pendingDelete.del", 1L, "1", Version.LATEST); private static final Store.MetadataSnapshot COMMIT_SNAPSHOT = new Store.MetadataSnapshot( @@ -41,7 +41,7 @@ public class CopyStateTests extends IndexShardTestCase { ); private static final Store.MetadataSnapshot SI_SNAPSHOT = new Store.MetadataSnapshot( - Map.of(SEGMENTS_FILE.name(), SEGMENTS_FILE), + Map.of(SEGMENT_FILE.name(), SEGMENT_FILE), null, 0 ); @@ -61,10 +61,6 @@ public void testCopyStateCreation() throws IOException { // version was never set so this should be zero assertEquals(0, checkpoint.getSegmentInfosVersion()); assertEquals(EXPECTED_LONG_VALUE, checkpoint.getPrimaryTerm()); - - Set pendingDeleteFiles = copyState.getPendingDeleteFiles(); - assertEquals(1, pendingDeleteFiles.size()); - assertTrue(pendingDeleteFiles.contains(PENDING_DELETE_FILE)); } public static IndexShard createMockIndexShard() throws IOException { @@ -78,7 +74,7 @@ public static IndexShard createMockIndexShard() throws IOException { SegmentInfos testSegmentInfos = new SegmentInfos(Version.LATEST.major); when(mockShard.getSegmentInfosSnapshot()).thenReturn(new GatedCloseable<>(testSegmentInfos, () -> {})); - when(mockStore.getMetadata(testSegmentInfos)).thenReturn(SI_SNAPSHOT); + when(mockStore.getSegmentMetadataMap(testSegmentInfos)).thenReturn(SI_SNAPSHOT.asMap()); IndexCommit mockIndexCommit = mock(IndexCommit.class); when(mockShard.acquireLastIndexCommit(false)).thenReturn(new GatedCloseable<>(mockIndexCommit, () -> {})); diff --git a/test/framework/src/main/java/org/opensearch/index/shard/IndexShardTestCase.java b/test/framework/src/main/java/org/opensearch/index/shard/IndexShardTestCase.java index 0838a1fe87aa4..073dc4b84472e 100644 --- a/test/framework/src/main/java/org/opensearch/index/shard/IndexShardTestCase.java +++ b/test/framework/src/main/java/org/opensearch/index/shard/IndexShardTestCase.java @@ -134,6 +134,7 @@ import java.io.IOException; import java.util.ArrayList; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; @@ -1200,12 +1201,7 @@ public void getCheckpointMetadata( try { final CopyState copyState = new CopyState(ReplicationCheckpoint.empty(primaryShard.shardId), primaryShard); listener.onResponse( - new CheckpointInfoResponse( - copyState.getCheckpoint(), - copyState.getMetadataSnapshot(), - copyState.getInfosBytes(), - copyState.getPendingDeleteFiles() - ) + new CheckpointInfoResponse(copyState.getCheckpoint(), copyState.getMetadataMap(), copyState.getInfosBytes()) ); copyState.decRef(); } catch (IOException e) { From 1889d966542355aea0a3839931cf4525e429208d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Sep 2022 13:37:51 -0400 Subject: [PATCH 105/187] Bump org.gradle.test-retry from 1.4.0 to 1.4.1 (#4411) * Bump org.gradle.test-retry from 1.4.0 to 1.4.1 Bumps org.gradle.test-retry from 1.4.0 to 1.4.1. --- updated-dependencies: - dependency-name: org.gradle.test-retry dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * Update changelog Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: dependabot[bot] --- CHANGELOG.md | 4 +++- build.gradle | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2b6528783a39..d04b754531b0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Support for HTTP/2 (server-side) ([#3847](https://github.com/opensearch-project/OpenSearch/pull/3847)) - BWC version 2.2.2 ([#4383](https://github.com/opensearch-project/OpenSearch/pull/4383)) - Support for labels on version bump PRs, skip label support for changelog verifier ([#4391](https://github.com/opensearch-project/OpenSearch/pull/4391)) +### Dependencies +- Bumps `org.gradle.test-retry` from 1.4.0 to 1.4.1 ### Dependencies - Bumps `com.diffplug.spotless` from 6.9.1 to 6.10.0 @@ -69,4 +71,4 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) [Unreleased]: https://github.com/opensearch-project/OpenSearch/compare/2.2.0...HEAD -[2.x]: https://github.com/opensearch-project/OpenSearch/compare/2.2.0...2.x +[2.x]: https://github.com/opensearch-project/OpenSearch/compare/2.2.0...2.x \ No newline at end of file diff --git a/build.gradle b/build.gradle index a1f4f2d04883a..56c5610124958 100644 --- a/build.gradle +++ b/build.gradle @@ -56,7 +56,7 @@ plugins { id 'opensearch.docker-support' id 'opensearch.global-build-info' id "com.diffplug.spotless" version "6.10.0" apply false - id "org.gradle.test-retry" version "1.4.0" apply false + id "org.gradle.test-retry" version "1.4.1" apply false id "test-report-aggregation" id 'jacoco-report-aggregation' } From fb64a856cd3e097b42a43a2fbb9ec659965ea9d5 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Tue, 6 Sep 2022 18:06:45 -0400 Subject: [PATCH 106/187] Revert to Netty 4.1.79.Final (#4428) Signed-off-by: Craig Perkins Signed-off-by: Craig Perkins --- CHANGELOG.md | 1 - buildSrc/version.properties | 2 +- modules/transport-netty4/build.gradle | 8 -------- .../licenses/netty-buffer-4.1.79.Final.jar.sha1 | 1 + .../licenses/netty-buffer-4.1.80.Final.jar.sha1 | 1 - .../licenses/netty-codec-4.1.79.Final.jar.sha1 | 1 + .../licenses/netty-codec-4.1.80.Final.jar.sha1 | 1 - .../licenses/netty-codec-http-4.1.79.Final.jar.sha1 | 1 + .../licenses/netty-codec-http-4.1.80.Final.jar.sha1 | 1 - .../licenses/netty-codec-http2-4.1.79.Final.jar.sha1 | 1 + .../licenses/netty-codec-http2-4.1.80.Final.jar.sha1 | 1 - .../licenses/netty-common-4.1.79.Final.jar.sha1 | 1 + .../licenses/netty-common-4.1.80.Final.jar.sha1 | 1 - .../licenses/netty-handler-4.1.79.Final.jar.sha1 | 1 + .../licenses/netty-handler-4.1.80.Final.jar.sha1 | 1 - .../licenses/netty-resolver-4.1.79.Final.jar.sha1 | 1 + .../licenses/netty-resolver-4.1.80.Final.jar.sha1 | 1 - .../licenses/netty-transport-4.1.79.Final.jar.sha1 | 1 + .../licenses/netty-transport-4.1.80.Final.jar.sha1 | 1 - ...tty-transport-native-unix-common-4.1.79.Final.jar.sha1 | 1 + ...tty-transport-native-unix-common-4.1.80.Final.jar.sha1 | 1 - .../licenses/netty-codec-dns-4.1.79.Final.jar.sha1 | 1 + .../licenses/netty-codec-dns-4.1.80.Final.jar.sha1 | 1 - .../licenses/netty-codec-http2-4.1.79.Final.jar.sha1 | 1 + .../licenses/netty-codec-http2-4.1.80.Final.jar.sha1 | 1 - .../licenses/netty-codec-socks-4.1.79.Final.jar.sha1 | 1 + .../licenses/netty-codec-socks-4.1.80.Final.jar.sha1 | 1 - .../licenses/netty-handler-proxy-4.1.79.Final.jar.sha1 | 1 + .../licenses/netty-handler-proxy-4.1.80.Final.jar.sha1 | 1 - .../licenses/netty-resolver-dns-4.1.79.Final.jar.sha1 | 1 + .../licenses/netty-resolver-dns-4.1.80.Final.jar.sha1 | 1 - ...tty-transport-native-unix-common-4.1.79.Final.jar.sha1 | 1 + ...tty-transport-native-unix-common-4.1.80.Final.jar.sha1 | 1 - .../licenses/netty-all-4.1.79.Final.jar.sha1 | 1 + .../licenses/netty-all-4.1.80.Final.jar.sha1 | 1 - plugins/transport-nio/build.gradle | 6 ------ .../licenses/netty-buffer-4.1.79.Final.jar.sha1 | 1 + .../licenses/netty-buffer-4.1.80.Final.jar.sha1 | 1 - .../licenses/netty-codec-4.1.79.Final.jar.sha1 | 1 + .../licenses/netty-codec-4.1.80.Final.jar.sha1 | 1 - .../licenses/netty-codec-http-4.1.79.Final.jar.sha1 | 1 + .../licenses/netty-codec-http-4.1.80.Final.jar.sha1 | 1 - .../licenses/netty-common-4.1.79.Final.jar.sha1 | 1 + .../licenses/netty-common-4.1.80.Final.jar.sha1 | 1 - .../licenses/netty-handler-4.1.79.Final.jar.sha1 | 1 + .../licenses/netty-handler-4.1.80.Final.jar.sha1 | 1 - .../licenses/netty-resolver-4.1.79.Final.jar.sha1 | 1 + .../licenses/netty-resolver-4.1.80.Final.jar.sha1 | 1 - .../licenses/netty-transport-4.1.79.Final.jar.sha1 | 1 + .../licenses/netty-transport-4.1.80.Final.jar.sha1 | 1 - 50 files changed, 24 insertions(+), 39 deletions(-) create mode 100644 modules/transport-netty4/licenses/netty-buffer-4.1.79.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-buffer-4.1.80.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-codec-4.1.79.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-codec-4.1.80.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-codec-http-4.1.79.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-codec-http-4.1.80.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-codec-http2-4.1.79.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-codec-http2-4.1.80.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-common-4.1.79.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-common-4.1.80.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-handler-4.1.79.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-handler-4.1.80.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-resolver-4.1.79.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-resolver-4.1.80.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-transport-4.1.79.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-transport-4.1.80.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-transport-native-unix-common-4.1.79.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-transport-native-unix-common-4.1.80.Final.jar.sha1 create mode 100644 plugins/repository-azure/licenses/netty-codec-dns-4.1.79.Final.jar.sha1 delete mode 100644 plugins/repository-azure/licenses/netty-codec-dns-4.1.80.Final.jar.sha1 create mode 100644 plugins/repository-azure/licenses/netty-codec-http2-4.1.79.Final.jar.sha1 delete mode 100644 plugins/repository-azure/licenses/netty-codec-http2-4.1.80.Final.jar.sha1 create mode 100644 plugins/repository-azure/licenses/netty-codec-socks-4.1.79.Final.jar.sha1 delete mode 100644 plugins/repository-azure/licenses/netty-codec-socks-4.1.80.Final.jar.sha1 create mode 100644 plugins/repository-azure/licenses/netty-handler-proxy-4.1.79.Final.jar.sha1 delete mode 100644 plugins/repository-azure/licenses/netty-handler-proxy-4.1.80.Final.jar.sha1 create mode 100644 plugins/repository-azure/licenses/netty-resolver-dns-4.1.79.Final.jar.sha1 delete mode 100644 plugins/repository-azure/licenses/netty-resolver-dns-4.1.80.Final.jar.sha1 create mode 100644 plugins/repository-azure/licenses/netty-transport-native-unix-common-4.1.79.Final.jar.sha1 delete mode 100644 plugins/repository-azure/licenses/netty-transport-native-unix-common-4.1.80.Final.jar.sha1 create mode 100644 plugins/repository-hdfs/licenses/netty-all-4.1.79.Final.jar.sha1 delete mode 100644 plugins/repository-hdfs/licenses/netty-all-4.1.80.Final.jar.sha1 create mode 100644 plugins/transport-nio/licenses/netty-buffer-4.1.79.Final.jar.sha1 delete mode 100644 plugins/transport-nio/licenses/netty-buffer-4.1.80.Final.jar.sha1 create mode 100644 plugins/transport-nio/licenses/netty-codec-4.1.79.Final.jar.sha1 delete mode 100644 plugins/transport-nio/licenses/netty-codec-4.1.80.Final.jar.sha1 create mode 100644 plugins/transport-nio/licenses/netty-codec-http-4.1.79.Final.jar.sha1 delete mode 100644 plugins/transport-nio/licenses/netty-codec-http-4.1.80.Final.jar.sha1 create mode 100644 plugins/transport-nio/licenses/netty-common-4.1.79.Final.jar.sha1 delete mode 100644 plugins/transport-nio/licenses/netty-common-4.1.80.Final.jar.sha1 create mode 100644 plugins/transport-nio/licenses/netty-handler-4.1.79.Final.jar.sha1 delete mode 100644 plugins/transport-nio/licenses/netty-handler-4.1.80.Final.jar.sha1 create mode 100644 plugins/transport-nio/licenses/netty-resolver-4.1.79.Final.jar.sha1 delete mode 100644 plugins/transport-nio/licenses/netty-resolver-4.1.80.Final.jar.sha1 create mode 100644 plugins/transport-nio/licenses/netty-transport-4.1.79.Final.jar.sha1 delete mode 100644 plugins/transport-nio/licenses/netty-transport-4.1.80.Final.jar.sha1 diff --git a/CHANGELOG.md b/CHANGELOG.md index d04b754531b0e..b9082ed039712 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,7 +23,6 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Dependency updates (httpcore, mockito, slf4j, httpasyncclient, commons-codec) ([#4308](https://github.com/opensearch-project/OpenSearch/pull/4308)) - Use RemoteSegmentStoreDirectory instead of RemoteDirectory ([#4240](https://github.com/opensearch-project/OpenSearch/pull/4240)) - Plugin ZIP publication groupId value is configurable ([#4156](https://github.com/opensearch-project/OpenSearch/pull/4156)) -- Update to Netty 4.1.80.Final ([#4359](https://github.com/opensearch-project/OpenSearch/pull/4359)) - Add index specific setting for remote repository ([#4253](https://github.com/opensearch-project/OpenSearch/pull/4253)) ### Deprecated diff --git a/buildSrc/version.properties b/buildSrc/version.properties index 6cc24a3f09244..072dcc4578977 100644 --- a/buildSrc/version.properties +++ b/buildSrc/version.properties @@ -21,7 +21,7 @@ asm = 9.3 # when updating the JNA version, also update the version in buildSrc/build.gradle jna = 5.5.0 -netty = 4.1.80.Final +netty = 4.1.79.Final joda = 2.10.13 # client dependencies diff --git a/modules/transport-netty4/build.gradle b/modules/transport-netty4/build.gradle index 8bbe0bf2ef65f..5d2047d7f18a2 100644 --- a/modules/transport-netty4/build.gradle +++ b/modules/transport-netty4/build.gradle @@ -144,14 +144,6 @@ thirdPartyAudit { 'org.apache.log4j.Level', 'org.apache.log4j.Logger', - // from io.netty.handler.ssl.OpenSslEngine (netty) - 'org.bouncycastle.openssl.PEMEncryptedKeyPair', - 'org.bouncycastle.openssl.PEMParser', - 'org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter', - 'org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder', - 'org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder', - 'org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo', - // from io.netty.handler.ssl.OpenSslEngine (netty) 'io.netty.internal.tcnative.Buffer', 'io.netty.internal.tcnative.CertificateCompressionAlgo', diff --git a/modules/transport-netty4/licenses/netty-buffer-4.1.79.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-buffer-4.1.79.Final.jar.sha1 new file mode 100644 index 0000000000000..8e9e4d0b7f754 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-buffer-4.1.79.Final.jar.sha1 @@ -0,0 +1 @@ +6c014412b599489b1db27c6bc08d8a46da94e397 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-buffer-4.1.80.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-buffer-4.1.80.Final.jar.sha1 deleted file mode 100644 index 471fe8b211df2..0000000000000 --- a/modules/transport-netty4/licenses/netty-buffer-4.1.80.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -a087321a63d9991e25f7b7d24ef53edcbcb954ff \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-codec-4.1.79.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-codec-4.1.79.Final.jar.sha1 new file mode 100644 index 0000000000000..c0920231d79a8 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-codec-4.1.79.Final.jar.sha1 @@ -0,0 +1 @@ +18f5b02af7ca611978bc28f2cb58cbb3b9b0f0ef \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-codec-4.1.80.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-codec-4.1.80.Final.jar.sha1 deleted file mode 100644 index 0f8e3bebe1532..0000000000000 --- a/modules/transport-netty4/licenses/netty-codec-4.1.80.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -4941821a158d16311665d8606aefa610ecf0f64c \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-codec-http-4.1.79.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-codec-http-4.1.79.Final.jar.sha1 new file mode 100644 index 0000000000000..a3f650da5abbd --- /dev/null +++ b/modules/transport-netty4/licenses/netty-codec-http-4.1.79.Final.jar.sha1 @@ -0,0 +1 @@ +882c70bc0a30a98bf3ce477f043e967ac026044c \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-codec-http-4.1.80.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-codec-http-4.1.80.Final.jar.sha1 deleted file mode 100644 index d18720d164335..0000000000000 --- a/modules/transport-netty4/licenses/netty-codec-http-4.1.80.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -efb23f9d5187d2f733595ef7930137f0cb2cec48 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-codec-http2-4.1.79.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-codec-http2-4.1.79.Final.jar.sha1 new file mode 100644 index 0000000000000..f2989024cfce1 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-codec-http2-4.1.79.Final.jar.sha1 @@ -0,0 +1 @@ +0eeffab0cd5efb699d5e4ab9b694d32fef6694b3 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-codec-http2-4.1.80.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-codec-http2-4.1.80.Final.jar.sha1 deleted file mode 100644 index d96a286b98493..0000000000000 --- a/modules/transport-netty4/licenses/netty-codec-http2-4.1.80.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -bf7b66834188ef1a6f6095291c6b81a1880798ba \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-common-4.1.79.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-common-4.1.79.Final.jar.sha1 new file mode 100644 index 0000000000000..faa7b099406a3 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-common-4.1.79.Final.jar.sha1 @@ -0,0 +1 @@ +2814bd465731355323aba0fdd22163bfce638a75 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-common-4.1.80.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-common-4.1.80.Final.jar.sha1 deleted file mode 100644 index d256e77b7024c..0000000000000 --- a/modules/transport-netty4/licenses/netty-common-4.1.80.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -3d43ce22863bc590e4e33fbdabbb58dc05f4c43d \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-handler-4.1.79.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-handler-4.1.79.Final.jar.sha1 new file mode 100644 index 0000000000000..8e314f164da69 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-handler-4.1.79.Final.jar.sha1 @@ -0,0 +1 @@ +2dc22423c8ed19906615fb936a5fcb7db14a4e6c \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-handler-4.1.80.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-handler-4.1.80.Final.jar.sha1 deleted file mode 100644 index 022ad6bc93dba..0000000000000 --- a/modules/transport-netty4/licenses/netty-handler-4.1.80.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -cf7029d2f9bc4eeae8ff15af7a528d06b518a017 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-resolver-4.1.79.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-resolver-4.1.79.Final.jar.sha1 new file mode 100644 index 0000000000000..af550935bb911 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-resolver-4.1.79.Final.jar.sha1 @@ -0,0 +1 @@ +55ecb1ff4464b56564a90824a741c3911264aaa4 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-resolver-4.1.80.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-resolver-4.1.80.Final.jar.sha1 deleted file mode 100644 index ad0f71b569377..0000000000000 --- a/modules/transport-netty4/licenses/netty-resolver-4.1.80.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -3bbb0d4bfbbab867e5b757b97a6e5e0d1348d94c \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-transport-4.1.79.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-transport-4.1.79.Final.jar.sha1 new file mode 100644 index 0000000000000..c6e18efb3ad3d --- /dev/null +++ b/modules/transport-netty4/licenses/netty-transport-4.1.79.Final.jar.sha1 @@ -0,0 +1 @@ +6cc2b49749b4fbcc39c687027e04e65e857552a9 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-transport-4.1.80.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-transport-4.1.80.Final.jar.sha1 deleted file mode 100644 index 2bfb4f377d89b..0000000000000 --- a/modules/transport-netty4/licenses/netty-transport-4.1.80.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -57fcace7a1b8567aa39921c915d1b1ba78fd4d2d \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-transport-native-unix-common-4.1.79.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-transport-native-unix-common-4.1.79.Final.jar.sha1 new file mode 100644 index 0000000000000..7f984663dfa85 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-transport-native-unix-common-4.1.79.Final.jar.sha1 @@ -0,0 +1 @@ +731937caec938b77b39df932a8da8aaca8d5ec05 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-transport-native-unix-common-4.1.80.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-transport-native-unix-common-4.1.80.Final.jar.sha1 deleted file mode 100644 index 998e6e8560724..0000000000000 --- a/modules/transport-netty4/licenses/netty-transport-native-unix-common-4.1.80.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -da3d7da1a8d317ae2c82b400fd255fe610c43ebe \ No newline at end of file diff --git a/plugins/repository-azure/licenses/netty-codec-dns-4.1.79.Final.jar.sha1 b/plugins/repository-azure/licenses/netty-codec-dns-4.1.79.Final.jar.sha1 new file mode 100644 index 0000000000000..a1753b194ea31 --- /dev/null +++ b/plugins/repository-azure/licenses/netty-codec-dns-4.1.79.Final.jar.sha1 @@ -0,0 +1 @@ +6c19c46f9529791964f636c93cfaca0556f0d5d0 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/netty-codec-dns-4.1.80.Final.jar.sha1 b/plugins/repository-azure/licenses/netty-codec-dns-4.1.80.Final.jar.sha1 deleted file mode 100644 index 2dab7f40b02b7..0000000000000 --- a/plugins/repository-azure/licenses/netty-codec-dns-4.1.80.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -6926d2ea779f41071ecb1948d880dfbb3a6ee126 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/netty-codec-http2-4.1.79.Final.jar.sha1 b/plugins/repository-azure/licenses/netty-codec-http2-4.1.79.Final.jar.sha1 new file mode 100644 index 0000000000000..f2989024cfce1 --- /dev/null +++ b/plugins/repository-azure/licenses/netty-codec-http2-4.1.79.Final.jar.sha1 @@ -0,0 +1 @@ +0eeffab0cd5efb699d5e4ab9b694d32fef6694b3 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/netty-codec-http2-4.1.80.Final.jar.sha1 b/plugins/repository-azure/licenses/netty-codec-http2-4.1.80.Final.jar.sha1 deleted file mode 100644 index d96a286b98493..0000000000000 --- a/plugins/repository-azure/licenses/netty-codec-http2-4.1.80.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -bf7b66834188ef1a6f6095291c6b81a1880798ba \ No newline at end of file diff --git a/plugins/repository-azure/licenses/netty-codec-socks-4.1.79.Final.jar.sha1 b/plugins/repository-azure/licenses/netty-codec-socks-4.1.79.Final.jar.sha1 new file mode 100644 index 0000000000000..913f0e7685c86 --- /dev/null +++ b/plugins/repository-azure/licenses/netty-codec-socks-4.1.79.Final.jar.sha1 @@ -0,0 +1 @@ +794a5937cdb1871c4ae350610752dec2929dc1d6 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/netty-codec-socks-4.1.80.Final.jar.sha1 b/plugins/repository-azure/licenses/netty-codec-socks-4.1.80.Final.jar.sha1 deleted file mode 100644 index 625344e6cfb0a..0000000000000 --- a/plugins/repository-azure/licenses/netty-codec-socks-4.1.80.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -00025b767be3425f3b31a34ee095c85619169f17 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/netty-handler-proxy-4.1.79.Final.jar.sha1 b/plugins/repository-azure/licenses/netty-handler-proxy-4.1.79.Final.jar.sha1 new file mode 100644 index 0000000000000..dbb072f3f665f --- /dev/null +++ b/plugins/repository-azure/licenses/netty-handler-proxy-4.1.79.Final.jar.sha1 @@ -0,0 +1 @@ +054aace8683de7893cf28d4aab72cd60f49b5700 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/netty-handler-proxy-4.1.80.Final.jar.sha1 b/plugins/repository-azure/licenses/netty-handler-proxy-4.1.80.Final.jar.sha1 deleted file mode 100644 index c3184ec5ff7d3..0000000000000 --- a/plugins/repository-azure/licenses/netty-handler-proxy-4.1.80.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -9b3b42ff805723fb98120f5ab2019c53e71da91b \ No newline at end of file diff --git a/plugins/repository-azure/licenses/netty-resolver-dns-4.1.79.Final.jar.sha1 b/plugins/repository-azure/licenses/netty-resolver-dns-4.1.79.Final.jar.sha1 new file mode 100644 index 0000000000000..a5d1be00d9c29 --- /dev/null +++ b/plugins/repository-azure/licenses/netty-resolver-dns-4.1.79.Final.jar.sha1 @@ -0,0 +1 @@ +8eb9be9b6a66a03f5f4df67fe559cb676493d167 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/netty-resolver-dns-4.1.80.Final.jar.sha1 b/plugins/repository-azure/licenses/netty-resolver-dns-4.1.80.Final.jar.sha1 deleted file mode 100644 index bb6a3502a729f..0000000000000 --- a/plugins/repository-azure/licenses/netty-resolver-dns-4.1.80.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -6b1602f80b6235b0b7d53bc5e9c1a6cd11c1b804 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/netty-transport-native-unix-common-4.1.79.Final.jar.sha1 b/plugins/repository-azure/licenses/netty-transport-native-unix-common-4.1.79.Final.jar.sha1 new file mode 100644 index 0000000000000..7f984663dfa85 --- /dev/null +++ b/plugins/repository-azure/licenses/netty-transport-native-unix-common-4.1.79.Final.jar.sha1 @@ -0,0 +1 @@ +731937caec938b77b39df932a8da8aaca8d5ec05 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/netty-transport-native-unix-common-4.1.80.Final.jar.sha1 b/plugins/repository-azure/licenses/netty-transport-native-unix-common-4.1.80.Final.jar.sha1 deleted file mode 100644 index 998e6e8560724..0000000000000 --- a/plugins/repository-azure/licenses/netty-transport-native-unix-common-4.1.80.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -da3d7da1a8d317ae2c82b400fd255fe610c43ebe \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/netty-all-4.1.79.Final.jar.sha1 b/plugins/repository-hdfs/licenses/netty-all-4.1.79.Final.jar.sha1 new file mode 100644 index 0000000000000..724950db96f09 --- /dev/null +++ b/plugins/repository-hdfs/licenses/netty-all-4.1.79.Final.jar.sha1 @@ -0,0 +1 @@ +1c53cffaa14d61de523b167377843e35807292a7 \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/netty-all-4.1.80.Final.jar.sha1 b/plugins/repository-hdfs/licenses/netty-all-4.1.80.Final.jar.sha1 deleted file mode 100644 index ae6eb1d85f1ea..0000000000000 --- a/plugins/repository-hdfs/licenses/netty-all-4.1.80.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -39e73b76a3ec65df731b371179e15f2c3e4e7575 \ No newline at end of file diff --git a/plugins/transport-nio/build.gradle b/plugins/transport-nio/build.gradle index c5b401de60c8c..a7e8c42a4e2d3 100644 --- a/plugins/transport-nio/build.gradle +++ b/plugins/transport-nio/build.gradle @@ -83,12 +83,6 @@ thirdPartyAudit { 'org.bouncycastle.cert.X509v3CertificateBuilder', 'org.bouncycastle.cert.jcajce.JcaX509CertificateConverter', 'org.bouncycastle.operator.jcajce.JcaContentSignerBuilder', - 'org.bouncycastle.openssl.PEMEncryptedKeyPair', - 'org.bouncycastle.openssl.PEMParser', - 'org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter', - 'org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder', - 'org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder', - 'org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo', // from io.netty.handler.ssl.JettyNpnSslEngine (netty) 'org.eclipse.jetty.npn.NextProtoNego$ClientProvider', diff --git a/plugins/transport-nio/licenses/netty-buffer-4.1.79.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-buffer-4.1.79.Final.jar.sha1 new file mode 100644 index 0000000000000..8e9e4d0b7f754 --- /dev/null +++ b/plugins/transport-nio/licenses/netty-buffer-4.1.79.Final.jar.sha1 @@ -0,0 +1 @@ +6c014412b599489b1db27c6bc08d8a46da94e397 \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-buffer-4.1.80.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-buffer-4.1.80.Final.jar.sha1 deleted file mode 100644 index 471fe8b211df2..0000000000000 --- a/plugins/transport-nio/licenses/netty-buffer-4.1.80.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -a087321a63d9991e25f7b7d24ef53edcbcb954ff \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-codec-4.1.79.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-codec-4.1.79.Final.jar.sha1 new file mode 100644 index 0000000000000..c0920231d79a8 --- /dev/null +++ b/plugins/transport-nio/licenses/netty-codec-4.1.79.Final.jar.sha1 @@ -0,0 +1 @@ +18f5b02af7ca611978bc28f2cb58cbb3b9b0f0ef \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-codec-4.1.80.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-codec-4.1.80.Final.jar.sha1 deleted file mode 100644 index 0f8e3bebe1532..0000000000000 --- a/plugins/transport-nio/licenses/netty-codec-4.1.80.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -4941821a158d16311665d8606aefa610ecf0f64c \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-codec-http-4.1.79.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-codec-http-4.1.79.Final.jar.sha1 new file mode 100644 index 0000000000000..a3f650da5abbd --- /dev/null +++ b/plugins/transport-nio/licenses/netty-codec-http-4.1.79.Final.jar.sha1 @@ -0,0 +1 @@ +882c70bc0a30a98bf3ce477f043e967ac026044c \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-codec-http-4.1.80.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-codec-http-4.1.80.Final.jar.sha1 deleted file mode 100644 index d18720d164335..0000000000000 --- a/plugins/transport-nio/licenses/netty-codec-http-4.1.80.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -efb23f9d5187d2f733595ef7930137f0cb2cec48 \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-common-4.1.79.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-common-4.1.79.Final.jar.sha1 new file mode 100644 index 0000000000000..faa7b099406a3 --- /dev/null +++ b/plugins/transport-nio/licenses/netty-common-4.1.79.Final.jar.sha1 @@ -0,0 +1 @@ +2814bd465731355323aba0fdd22163bfce638a75 \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-common-4.1.80.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-common-4.1.80.Final.jar.sha1 deleted file mode 100644 index d256e77b7024c..0000000000000 --- a/plugins/transport-nio/licenses/netty-common-4.1.80.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -3d43ce22863bc590e4e33fbdabbb58dc05f4c43d \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-handler-4.1.79.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-handler-4.1.79.Final.jar.sha1 new file mode 100644 index 0000000000000..8e314f164da69 --- /dev/null +++ b/plugins/transport-nio/licenses/netty-handler-4.1.79.Final.jar.sha1 @@ -0,0 +1 @@ +2dc22423c8ed19906615fb936a5fcb7db14a4e6c \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-handler-4.1.80.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-handler-4.1.80.Final.jar.sha1 deleted file mode 100644 index 022ad6bc93dba..0000000000000 --- a/plugins/transport-nio/licenses/netty-handler-4.1.80.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -cf7029d2f9bc4eeae8ff15af7a528d06b518a017 \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-resolver-4.1.79.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-resolver-4.1.79.Final.jar.sha1 new file mode 100644 index 0000000000000..af550935bb911 --- /dev/null +++ b/plugins/transport-nio/licenses/netty-resolver-4.1.79.Final.jar.sha1 @@ -0,0 +1 @@ +55ecb1ff4464b56564a90824a741c3911264aaa4 \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-resolver-4.1.80.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-resolver-4.1.80.Final.jar.sha1 deleted file mode 100644 index ad0f71b569377..0000000000000 --- a/plugins/transport-nio/licenses/netty-resolver-4.1.80.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -3bbb0d4bfbbab867e5b757b97a6e5e0d1348d94c \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-transport-4.1.79.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-transport-4.1.79.Final.jar.sha1 new file mode 100644 index 0000000000000..c6e18efb3ad3d --- /dev/null +++ b/plugins/transport-nio/licenses/netty-transport-4.1.79.Final.jar.sha1 @@ -0,0 +1 @@ +6cc2b49749b4fbcc39c687027e04e65e857552a9 \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-transport-4.1.80.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-transport-4.1.80.Final.jar.sha1 deleted file mode 100644 index 2bfb4f377d89b..0000000000000 --- a/plugins/transport-nio/licenses/netty-transport-4.1.80.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -57fcace7a1b8567aa39921c915d1b1ba78fd4d2d \ No newline at end of file From bff9d0fa6c5d56943a02344768ae37db54b1c71d Mon Sep 17 00:00:00 2001 From: Rishikesh Pasham <62345295+Rishikesh1159@users.noreply.github.com> Date: Wed, 7 Sep 2022 04:09:00 +0000 Subject: [PATCH 107/187] [Segment Replication] Fix timeout issue by calculating time needed to process getSegmentFiles. (#4426) * Fix timeout issue by calculating time needed to process getSegmentFiles. Signed-off-by: Rishikesh1159 * Formatting sizeOfSegmentFiles for time calculation. Signed-off-by: Rishikesh1159 * Addressing comments and applying spotless check. Signed-off-by: Rishikesh1159 Signed-off-by: Rishikesh1159 --- CHANGELOG.md | 3 ++- .../PrimaryShardReplicationSource.java | 18 +++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b9082ed039712..d8373e9904d7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Fix NoSuchFileExceptions with segment replication when computing primary metadata snapshots ([#4366](https://github.com/opensearch-project/OpenSearch/pull/4366)) - [Segment Replication] Update flaky testOnNewCheckpointFromNewPrimaryCancelOngoingReplication unit test ([#4414](https://github.com/opensearch-project/OpenSearch/pull/4414)) - Fixed the `_cat/shards/10_basic.yml` test cases fix. +- [Segment Replication] Fix timeout issue by calculating time needed to process getSegmentFiles ([#4426](https://github.com/opensearch-project/OpenSearch/pull/4426)) ### Security - CVE-2022-25857 org.yaml:snakeyaml DOS vulnerability ([#4341](https://github.com/opensearch-project/OpenSearch/pull/4341)) @@ -70,4 +71,4 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) [Unreleased]: https://github.com/opensearch-project/OpenSearch/compare/2.2.0...HEAD -[2.x]: https://github.com/opensearch-project/OpenSearch/compare/2.2.0...2.x \ No newline at end of file +[2.x]: https://github.com/opensearch-project/OpenSearch/compare/2.2.0...2.x diff --git a/server/src/main/java/org/opensearch/indices/replication/PrimaryShardReplicationSource.java b/server/src/main/java/org/opensearch/indices/replication/PrimaryShardReplicationSource.java index aa0b5416dd0ff..8107f99723eaf 100644 --- a/server/src/main/java/org/opensearch/indices/replication/PrimaryShardReplicationSource.java +++ b/server/src/main/java/org/opensearch/indices/replication/PrimaryShardReplicationSource.java @@ -13,11 +13,13 @@ import org.opensearch.action.ActionListener; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.common.io.stream.Writeable; +import org.opensearch.common.unit.TimeValue; import org.opensearch.index.store.Store; import org.opensearch.index.store.StoreFileMetadata; import org.opensearch.indices.recovery.RecoverySettings; import org.opensearch.indices.recovery.RetryableTransportClient; import org.opensearch.indices.replication.checkpoint.ReplicationCheckpoint; +import org.opensearch.transport.TransportRequestOptions; import org.opensearch.transport.TransportService; import java.util.List; @@ -78,6 +80,17 @@ public void getSegmentFiles( ) { final Writeable.Reader reader = GetSegmentFilesResponse::new; final ActionListener responseListener = ActionListener.map(listener, r -> r); + // Few of the below assumptions and calculations are added for experimental release of segment replication feature in 2.3 + // version. These will be changed in next release. + + // Storing the size of files to fetch in bytes. + final long sizeOfSegmentFiles = filesToFetch.stream().mapToLong(file -> file.length()).sum(); + + // Maximum size of files to fetch (segment files) in bytes, that can be processed in 1 minute for a m5.xlarge machine. + long baseSegmentFilesSize = 100000000; + + // Formula for calculating time needed to process a replication event's files to fetch process + final long timeToGetSegmentFiles = 1 + (sizeOfSegmentFiles / baseSegmentFilesSize); final GetSegmentFilesRequest request = new GetSegmentFilesRequest( replicationId, targetAllocationId, @@ -85,7 +98,10 @@ public void getSegmentFiles( filesToFetch, checkpoint ); - transportClient.executeRetryableAction(GET_SEGMENT_FILES, request, responseListener, reader); + final TransportRequestOptions options = TransportRequestOptions.builder() + .withTimeout(TimeValue.timeValueMinutes(timeToGetSegmentFiles)) + .build(); + transportClient.executeRetryableAction(GET_SEGMENT_FILES, request, options, responseListener, reader); } @Override From ccf575a135c8c7512d9a3cfc343a90ca37ea1d80 Mon Sep 17 00:00:00 2001 From: Andriy Redko Date: Wed, 7 Sep 2022 10:09:56 -0400 Subject: [PATCH 108/187] [Bug]: gradle check failing with java heap OutOfMemoryError (#4328) * [Bug]: gradle check failing with java heap OutOfMemoryError Signed-off-by: Andriy Redko * Fork JavaCompile task Signed-off-by: Andriy Redko Signed-off-by: Andriy Redko --- CHANGELOG.md | 1 + build.gradle | 10 +++++++ .../HierarchyCircuitBreakerService.java | 15 +++++++++-- .../settings/MemorySizeSettingsTests.java | 27 +++++++++++++------ 4 files changed, 43 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8373e9904d7f..bcbd7f9ca88e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - [Segment Replication] Update flaky testOnNewCheckpointFromNewPrimaryCancelOngoingReplication unit test ([#4414](https://github.com/opensearch-project/OpenSearch/pull/4414)) - Fixed the `_cat/shards/10_basic.yml` test cases fix. - [Segment Replication] Fix timeout issue by calculating time needed to process getSegmentFiles ([#4426](https://github.com/opensearch-project/OpenSearch/pull/4426)) +- [Bug]: gradle check failing with java heap OutOfMemoryError (([#4328](https://github.com/opensearch-project/OpenSearch/ ### Security - CVE-2022-25857 org.yaml:snakeyaml DOS vulnerability ([#4341](https://github.com/opensearch-project/OpenSearch/pull/4341)) diff --git a/build.gradle b/build.gradle index 56c5610124958..bcae5bc3884a7 100644 --- a/build.gradle +++ b/build.gradle @@ -264,6 +264,12 @@ tasks.register("branchConsistency") { allprojects { // configure compiler options tasks.withType(JavaCompile).configureEach { JavaCompile compile -> + options.fork = true + + configure(options.forkOptions) { + memoryMaximumSize = project.property('options.forkOptions.memoryMaximumSize') + } + // See please https://bugs.openjdk.java.net/browse/JDK-8209058 if (BuildParams.runtimeJavaVersion > JavaVersion.VERSION_11) { compile.options.compilerArgs << '-Werror' @@ -389,6 +395,10 @@ allprojects { // the dependency is added. gradle.projectsEvaluated { allprojects { + project.tasks.withType(JavaForkOptions) { + maxHeapSize project.property('options.forkOptions.memoryMaximumSize') + } + if (project.path == ':test:framework') { // :test:framework:test cannot run before and after :server:test return diff --git a/server/src/main/java/org/opensearch/indices/breaker/HierarchyCircuitBreakerService.java b/server/src/main/java/org/opensearch/indices/breaker/HierarchyCircuitBreakerService.java index c0056aab3fb16..40bb4894c7397 100644 --- a/server/src/main/java/org/opensearch/indices/breaker/HierarchyCircuitBreakerService.java +++ b/server/src/main/java/org/opensearch/indices/breaker/HierarchyCircuitBreakerService.java @@ -559,8 +559,19 @@ static long fallbackRegionSize(JvmInfo jvmInfo) { // https://hg.openjdk.java.net/jdk/jdk/file/e7d0ec2d06e8/src/hotspot/share/gc/g1/heapRegion.cpp#l67 // based on this JDK "bug": // https://bugs.openjdk.java.net/browse/JDK-8241670 - long averageHeapSize = (jvmInfo.getMem().getHeapMax().getBytes() + JvmInfo.jvmInfo().getMem().getHeapMax().getBytes()) / 2; - long regionSize = Long.highestOneBit(averageHeapSize / 2048); + // JDK-17 updates: + // https://github.com/openjdk/jdk17u/blob/master/src/hotspot/share/gc/g1/heapRegionBounds.hpp + // https://github.com/openjdk/jdk17u/blob/master/src/hotspot/share/gc/g1/heapRegion.cpp#L67 + long regionSizeUnrounded = Math.min( + Math.max(JvmInfo.jvmInfo().getMem().getHeapMax().getBytes() / 2048, ByteSizeUnit.MB.toBytes(1)), + ByteSizeUnit.MB.toBytes(32) + ); + + long regionSize = Long.highestOneBit(regionSizeUnrounded); + if (regionSize != regionSizeUnrounded) { + regionSize <<= 1; /* next power of 2 */ + } + if (regionSize < ByteSizeUnit.MB.toBytes(1)) { regionSize = ByteSizeUnit.MB.toBytes(1); } else if (regionSize > ByteSizeUnit.MB.toBytes(32)) { diff --git a/server/src/test/java/org/opensearch/common/settings/MemorySizeSettingsTests.java b/server/src/test/java/org/opensearch/common/settings/MemorySizeSettingsTests.java index f64b45e80dbca..2c7251818e2bc 100644 --- a/server/src/test/java/org/opensearch/common/settings/MemorySizeSettingsTests.java +++ b/server/src/test/java/org/opensearch/common/settings/MemorySizeSettingsTests.java @@ -33,7 +33,6 @@ package org.opensearch.common.settings; import org.opensearch.common.settings.Setting.Property; -import org.opensearch.common.unit.ByteSizeUnit; import org.opensearch.common.unit.ByteSizeValue; import org.opensearch.common.util.PageCacheRecycler; import org.opensearch.indices.IndexingMemoryController; @@ -83,9 +82,13 @@ public void testIndicesRequestCacheSetting() { } public void testCircuitBreakerSettings() { - // default is chosen based on actual heap size + final Settings settings = Settings.builder() + .put(HierarchyCircuitBreakerService.USE_REAL_MEMORY_USAGE_SETTING.getKey(), randomBoolean()) + .build(); + + // default is chosen based on USE_REAL_MEMORY_USAGE_SETTING setting double defaultTotalPercentage; - if (JvmInfo.jvmInfo().getMem().getHeapMax().getBytes() < new ByteSizeValue(1, ByteSizeUnit.GB).getBytes()) { + if (HierarchyCircuitBreakerService.USE_REAL_MEMORY_USAGE_SETTING.get(settings)) { defaultTotalPercentage = 0.95d; } else { defaultTotalPercentage = 0.7d; @@ -93,22 +96,26 @@ public void testCircuitBreakerSettings() { assertMemorySizeSetting( HierarchyCircuitBreakerService.TOTAL_CIRCUIT_BREAKER_LIMIT_SETTING, "indices.breaker.total.limit", - new ByteSizeValue((long) (JvmInfo.jvmInfo().getMem().getHeapMax().getBytes() * defaultTotalPercentage)) + new ByteSizeValue((long) (JvmInfo.jvmInfo().getMem().getHeapMax().getBytes() * defaultTotalPercentage)), + settings ); assertMemorySizeSetting( HierarchyCircuitBreakerService.FIELDDATA_CIRCUIT_BREAKER_LIMIT_SETTING, "indices.breaker.fielddata.limit", - new ByteSizeValue((long) (JvmInfo.jvmInfo().getMem().getHeapMax().getBytes() * 0.4)) + new ByteSizeValue((long) (JvmInfo.jvmInfo().getMem().getHeapMax().getBytes() * 0.4)), + settings ); assertMemorySizeSetting( HierarchyCircuitBreakerService.REQUEST_CIRCUIT_BREAKER_LIMIT_SETTING, "indices.breaker.request.limit", - new ByteSizeValue((long) (JvmInfo.jvmInfo().getMem().getHeapMax().getBytes() * 0.6)) + new ByteSizeValue((long) (JvmInfo.jvmInfo().getMem().getHeapMax().getBytes() * 0.6)), + settings ); assertMemorySizeSetting( HierarchyCircuitBreakerService.IN_FLIGHT_REQUESTS_CIRCUIT_BREAKER_LIMIT_SETTING, "network.breaker.inflight_requests.limit", - new ByteSizeValue((JvmInfo.jvmInfo().getMem().getHeapMax().getBytes())) + new ByteSizeValue((JvmInfo.jvmInfo().getMem().getHeapMax().getBytes())), + settings ); } @@ -121,10 +128,14 @@ public void testIndicesFieldDataCacheSetting() { } private void assertMemorySizeSetting(Setting setting, String settingKey, ByteSizeValue defaultValue) { + assertMemorySizeSetting(setting, settingKey, defaultValue, Settings.EMPTY); + } + + private void assertMemorySizeSetting(Setting setting, String settingKey, ByteSizeValue defaultValue, Settings settings) { assertThat(setting, notNullValue()); assertThat(setting.getKey(), equalTo(settingKey)); assertThat(setting.getProperties(), hasItem(Property.NodeScope)); - assertThat(setting.getDefault(Settings.EMPTY), equalTo(defaultValue)); + assertThat(setting.getDefault(settings), equalTo(defaultValue)); Settings settingWithPercentage = Settings.builder().put(settingKey, "25%").build(); assertThat( setting.get(settingWithPercentage), From c1a1f1aef9f3319c325cac686a1e06c1a479320f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 Sep 2022 13:16:08 -0400 Subject: [PATCH 109/187] Bump azure-core-http-netty from 1.12.0 to 1.12.4 in /plugins/repository-azure (#4160) * Bump azure-core-http-netty in /plugins/repository-azure Bumps [azure-core-http-netty](https://github.com/Azure/azure-sdk-for-java) from 1.12.0 to 1.12.4. - [Release notes](https://github.com/Azure/azure-sdk-for-java/releases) - [Commits](https://github.com/Azure/azure-sdk-for-java/compare/azure-core_1.12.0...azure-core-http-netty_1.12.4) --- updated-dependencies: - dependency-name: com.azure:azure-core-http-netty dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * Updating SHAs Signed-off-by: dependabot[bot] * Added missing forbidden classes Signed-off-by: Owais Kazi * update version of azure-storage-common and azure-core-http-netty Signed-off-by: Xue Zhou * update version of azure-storage-common and azure-core-http-netty Signed-off-by: Xue Zhou * adding changelog information Signed-off-by: Xue Zhou Signed-off-by: dependabot[bot] Signed-off-by: Owais Kazi Signed-off-by: Xue Zhou Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: dependabot[bot] Co-authored-by: Owais Kazi Co-authored-by: Xue Zhou --- CHANGELOG.md | 3 +++ plugins/repository-azure/build.gradle | 8 ++++---- .../repository-azure/licenses/azure-core-1.27.0.jar.sha1 | 1 - .../repository-azure/licenses/azure-core-1.31.0.jar.sha1 | 1 + .../licenses/azure-core-http-netty-1.12.0.jar.sha1 | 1 - .../licenses/azure-core-http-netty-1.12.4.jar.sha1 | 1 + .../licenses/azure-storage-common-12.16.0.jar.sha1 | 1 - .../licenses/azure-storage-common-12.18.0.jar.sha1 | 1 + 8 files changed, 10 insertions(+), 7 deletions(-) delete mode 100644 plugins/repository-azure/licenses/azure-core-1.27.0.jar.sha1 create mode 100644 plugins/repository-azure/licenses/azure-core-1.31.0.jar.sha1 delete mode 100644 plugins/repository-azure/licenses/azure-core-http-netty-1.12.0.jar.sha1 create mode 100644 plugins/repository-azure/licenses/azure-core-http-netty-1.12.4.jar.sha1 delete mode 100644 plugins/repository-azure/licenses/azure-storage-common-12.16.0.jar.sha1 create mode 100644 plugins/repository-azure/licenses/azure-storage-common-12.18.0.jar.sha1 diff --git a/CHANGELOG.md b/CHANGELOG.md index bcbd7f9ca88e1..e10f051dfe738 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,9 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### Dependencies - Bumps `com.diffplug.spotless` from 6.9.1 to 6.10.0 - Bumps `xmlbeans` from 5.1.0 to 5.1.1 +- Bumps azure-core-http-netty from 1.12.0 to 1.12.4([#4160](https://github.com/opensearch-project/OpenSearch/pull/4160)) +- Bumps azure-core from 1.27.0 to 1.31.0([#4160](https://github.com/opensearch-project/OpenSearch/pull/4160)) +- Bumps azure-storage-common from 12.16.0 to 12.18.0([#4160](https://github.com/opensearch-project/OpenSearch/pull/4160)) ### Changed - Dependency updates (httpcore, mockito, slf4j, httpasyncclient, commons-codec) ([#4308](https://github.com/opensearch-project/OpenSearch/pull/4308)) diff --git a/plugins/repository-azure/build.gradle b/plugins/repository-azure/build.gradle index 227d7d1b68977..1af2de4f176f2 100644 --- a/plugins/repository-azure/build.gradle +++ b/plugins/repository-azure/build.gradle @@ -44,9 +44,9 @@ opensearchplugin { } dependencies { - api 'com.azure:azure-core:1.27.0' - api 'com.azure:azure-storage-common:12.16.0' - api 'com.azure:azure-core-http-netty:1.12.0' + api 'com.azure:azure-core:1.31.0' + api 'com.azure:azure-storage-common:12.18.0' + api 'com.azure:azure-core-http-netty:1.12.4' api "io.netty:netty-codec-dns:${versions.netty}" api "io.netty:netty-codec-socks:${versions.netty}" api "io.netty:netty-codec-http2:${versions.netty}" @@ -137,7 +137,7 @@ thirdPartyAudit { 'javax.xml.bind.annotation.XmlAccessOrder', 'javax.xml.bind.annotation.XmlAccessType', 'javax.xml.bind.annotation.XmlAccessorOrder', - 'javax.xml.bind.annotation.XmlAccessorType', + 'javax.xml.bind.annotation.XmlAccessorType', 'javax.xml.bind.annotation.XmlAttribute', 'javax.xml.bind.annotation.XmlElement', 'javax.xml.bind.annotation.XmlElement$DEFAULT', diff --git a/plugins/repository-azure/licenses/azure-core-1.27.0.jar.sha1 b/plugins/repository-azure/licenses/azure-core-1.27.0.jar.sha1 deleted file mode 100644 index 9206b697ca648..0000000000000 --- a/plugins/repository-azure/licenses/azure-core-1.27.0.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -75a2db538d218e2bd3c2cbdf04c955b8f6db6626 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/azure-core-1.31.0.jar.sha1 b/plugins/repository-azure/licenses/azure-core-1.31.0.jar.sha1 new file mode 100644 index 0000000000000..6a5076b3da301 --- /dev/null +++ b/plugins/repository-azure/licenses/azure-core-1.31.0.jar.sha1 @@ -0,0 +1 @@ +39f18dae02237f90f1cd23b56701d7f9d9525531 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/azure-core-http-netty-1.12.0.jar.sha1 b/plugins/repository-azure/licenses/azure-core-http-netty-1.12.0.jar.sha1 deleted file mode 100644 index 1b5d162c004de..0000000000000 --- a/plugins/repository-azure/licenses/azure-core-http-netty-1.12.0.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -e4381e4e2801ee190ae76b61dbd992e94b40272e \ No newline at end of file diff --git a/plugins/repository-azure/licenses/azure-core-http-netty-1.12.4.jar.sha1 b/plugins/repository-azure/licenses/azure-core-http-netty-1.12.4.jar.sha1 new file mode 100644 index 0000000000000..5cb180b20cf8b --- /dev/null +++ b/plugins/repository-azure/licenses/azure-core-http-netty-1.12.4.jar.sha1 @@ -0,0 +1 @@ +70dcc08887f2d70a8f812bf00d4fa10390fab3fd \ No newline at end of file diff --git a/plugins/repository-azure/licenses/azure-storage-common-12.16.0.jar.sha1 b/plugins/repository-azure/licenses/azure-storage-common-12.16.0.jar.sha1 deleted file mode 100644 index ebf328aa69ee8..0000000000000 --- a/plugins/repository-azure/licenses/azure-storage-common-12.16.0.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -9f652b89a30269bdff6644468632726d4ba4fbd1 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/azure-storage-common-12.18.0.jar.sha1 b/plugins/repository-azure/licenses/azure-storage-common-12.18.0.jar.sha1 new file mode 100644 index 0000000000000..f824d6cdf4f18 --- /dev/null +++ b/plugins/repository-azure/licenses/azure-storage-common-12.18.0.jar.sha1 @@ -0,0 +1 @@ +cb6fa5863f5cd8406934baec739285209165ef4b \ No newline at end of file From ce6c6a80a69409e266ca901d367e6ba9708e75b7 Mon Sep 17 00:00:00 2001 From: mwilkinson-imo <47403752+mwilkinson-imo@users.noreply.github.com> Date: Wed, 7 Sep 2022 12:21:10 -0500 Subject: [PATCH 110/187] Bugfix: Allow opensearch.bat file and opensearch-env.bat files to run when install path includes a space. (#4362) * Bugfix: Prevent escaping of in `else-if` statement by setting variable without double quotes. Signed-off-by: Mike Wilkinson * Add changelog entry for fix Signed-off-by: Mike Wilkinson * Escape double quotes for environment variables set by `opensearch-env.bat`. Explicitly apply quotes where those environment variables are invoked. Signed-off-by: Mike Wilkinson Signed-off-by: Mike Wilkinson Co-authored-by: Mike Wilkinson --- CHANGELOG.md | 1 + distribution/src/bin/opensearch-cli.bat | 2 +- distribution/src/bin/opensearch-env.bat | 10 +++++----- distribution/src/bin/opensearch-service.bat | 4 ++-- distribution/src/bin/opensearch.bat | 6 +++--- 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e10f051dfe738..680f30eca0f20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,6 +51,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Fixed the `_cat/shards/10_basic.yml` test cases fix. - [Segment Replication] Fix timeout issue by calculating time needed to process getSegmentFiles ([#4426](https://github.com/opensearch-project/OpenSearch/pull/4426)) - [Bug]: gradle check failing with java heap OutOfMemoryError (([#4328](https://github.com/opensearch-project/OpenSearch/ +- `opensearch.bat` fails to execute when install path includes spaces ([#4362](https://github.com/opensearch-project/OpenSearch/pull/4362)) ### Security - CVE-2022-25857 org.yaml:snakeyaml DOS vulnerability ([#4341](https://github.com/opensearch-project/OpenSearch/pull/4341)) diff --git a/distribution/src/bin/opensearch-cli.bat b/distribution/src/bin/opensearch-cli.bat index 734669e1f9349..f080346a4478a 100644 --- a/distribution/src/bin/opensearch-cli.bat +++ b/distribution/src/bin/opensearch-cli.bat @@ -16,7 +16,7 @@ rem use a small heap size for the CLI tools, and thus the serial collector to rem avoid stealing many CPU cycles; a user can override by setting OPENSEARCH_JAVA_OPTS set OPENSEARCH_JAVA_OPTS=-Xms4m -Xmx64m -XX:+UseSerialGC %OPENSEARCH_JAVA_OPTS% -%JAVA% ^ +"%JAVA%" ^ %OPENSEARCH_JAVA_OPTS% ^ -Dopensearch.path.home="%OPENSEARCH_HOME%" ^ -Dopensearch.path.conf="%OPENSEARCH_PATH_CONF%" ^ diff --git a/distribution/src/bin/opensearch-env.bat b/distribution/src/bin/opensearch-env.bat index 96770f72f35c8..95088aaee7d3d 100644 --- a/distribution/src/bin/opensearch-env.bat +++ b/distribution/src/bin/opensearch-env.bat @@ -43,14 +43,14 @@ rem comparing to empty string makes this equivalent to bash -v check on env var rem and allows to effectively force use of the bundled jdk when launching OpenSearch rem by setting OPENSEARCH_JAVA_HOME= and JAVA_HOME= if not "%OPENSEARCH_JAVA_HOME%" == "" ( - set JAVA="%OPENSEARCH_JAVA_HOME%\bin\java.exe" + set "JAVA=%OPENSEARCH_JAVA_HOME%\bin\java.exe" set JAVA_TYPE=OPENSEARCH_JAVA_HOME ) else if not "%JAVA_HOME%" == "" ( - set JAVA="%JAVA_HOME%\bin\java.exe" + set "JAVA=%JAVA_HOME%\bin\java.exe" set JAVA_TYPE=JAVA_HOME ) else ( - set JAVA="%OPENSEARCH_HOME%\jdk\bin\java.exe" - set JAVA_HOME="%OPENSEARCH_HOME%\jdk" + set "JAVA=%OPENSEARCH_HOME%\jdk\bin\java.exe" + set "JAVA_HOME=%OPENSEARCH_HOME%\jdk" set JAVA_TYPE=bundled jdk ) @@ -73,4 +73,4 @@ if defined JAVA_OPTS ( ) rem check the Java version -%JAVA% -cp "%OPENSEARCH_CLASSPATH%" "org.opensearch.tools.java_version_checker.JavaVersionChecker" || exit /b 1 +"%JAVA%" -cp "%OPENSEARCH_CLASSPATH%" "org.opensearch.tools.java_version_checker.JavaVersionChecker" || exit /b 1 diff --git a/distribution/src/bin/opensearch-service.bat b/distribution/src/bin/opensearch-service.bat index a11dc8316e8b1..c1f3f264ec4a0 100644 --- a/distribution/src/bin/opensearch-service.bat +++ b/distribution/src/bin/opensearch-service.bat @@ -121,7 +121,7 @@ if exist "%JAVA_HOME%\bin\server\jvm.dll" ( :foundJVM if not defined OPENSEARCH_TMPDIR ( - for /f "tokens=* usebackq" %%a in (`CALL %JAVA% -cp "!OPENSEARCH_CLASSPATH!" "org.opensearch.tools.launchers.TempDirectory"`) do set OPENSEARCH_TMPDIR=%%a + for /f "tokens=* usebackq" %%a in (`CALL "%JAVA%" -cp "!OPENSEARCH_CLASSPATH!" "org.opensearch.tools.launchers.TempDirectory"`) do set OPENSEARCH_TMPDIR=%%a ) rem The JVM options parser produces the final JVM options to start @@ -135,7 +135,7 @@ rem - third, JVM options from OPENSEARCH_JAVA_OPTS are applied rem - fourth, ergonomic JVM options are applied @setlocal -for /F "usebackq delims=" %%a in (`CALL %JAVA% -cp "!OPENSEARCH_CLASSPATH!" "org.opensearch.tools.launchers.JvmOptionsParser" "!OPENSEARCH_PATH_CONF!" ^|^| echo jvm_options_parser_failed`) do set OPENSEARCH_JAVA_OPTS=%%a +for /F "usebackq delims=" %%a in (`CALL "%JAVA%" -cp "!OPENSEARCH_CLASSPATH!" "org.opensearch.tools.launchers.JvmOptionsParser" "!OPENSEARCH_PATH_CONF!" ^|^| echo jvm_options_parser_failed`) do set OPENSEARCH_JAVA_OPTS=%%a @endlocal & set "MAYBE_JVM_OPTIONS_PARSER_FAILED=%OPENSEARCH_JAVA_OPTS%" & set OPENSEARCH_JAVA_OPTS=%OPENSEARCH_JAVA_OPTS% if "%MAYBE_JVM_OPTIONS_PARSER_FAILED%" == "jvm_options_parser_failed" ( diff --git a/distribution/src/bin/opensearch.bat b/distribution/src/bin/opensearch.bat index dda15124e1654..cce21504c55b7 100644 --- a/distribution/src/bin/opensearch.bat +++ b/distribution/src/bin/opensearch.bat @@ -75,7 +75,7 @@ IF "%checkpassword%"=="Y" ( ) if not defined OPENSEARCH_TMPDIR ( - for /f "tokens=* usebackq" %%a in (`CALL %JAVA% -cp "!OPENSEARCH_CLASSPATH!" "org.opensearch.tools.launchers.TempDirectory"`) do set OPENSEARCH_TMPDIR=%%a + for /f "tokens=* usebackq" %%a in (`CALL "%JAVA%" -cp "!OPENSEARCH_CLASSPATH!" "org.opensearch.tools.launchers.TempDirectory"`) do set OPENSEARCH_TMPDIR=%%a ) rem The JVM options parser produces the final JVM options to start @@ -88,7 +88,7 @@ rem jvm.options.d/*.options rem - third, JVM options from OPENSEARCH_JAVA_OPTS are applied rem - fourth, ergonomic JVM options are applied @setlocal -for /F "usebackq delims=" %%a in (`CALL %JAVA% -cp "!OPENSEARCH_CLASSPATH!" "org.opensearch.tools.launchers.JvmOptionsParser" "!OPENSEARCH_PATH_CONF!" ^|^| echo jvm_options_parser_failed`) do set OPENSEARCH_JAVA_OPTS=%%a +for /F "usebackq delims=" %%a in (`CALL "%JAVA%" -cp "!OPENSEARCH_CLASSPATH!" "org.opensearch.tools.launchers.JvmOptionsParser" "!OPENSEARCH_PATH_CONF!" ^|^| echo jvm_options_parser_failed`) do set OPENSEARCH_JAVA_OPTS=%%a @endlocal & set "MAYBE_JVM_OPTIONS_PARSER_FAILED=%OPENSEARCH_JAVA_OPTS%" & set OPENSEARCH_JAVA_OPTS=%OPENSEARCH_JAVA_OPTS% if "%MAYBE_JVM_OPTIONS_PARSER_FAILED%" == "jvm_options_parser_failed" ( @@ -103,7 +103,7 @@ SET KEYSTORE_PASSWORD=!KEYSTORE_PASSWORD:^<=^^^=^^^>! SET KEYSTORE_PASSWORD=!KEYSTORE_PASSWORD:^\=^^^\! -ECHO.!KEYSTORE_PASSWORD!| %JAVA% %OPENSEARCH_JAVA_OPTS% -Dopensearch ^ +ECHO.!KEYSTORE_PASSWORD!| "%JAVA%" %OPENSEARCH_JAVA_OPTS% -Dopensearch ^ -Dopensearch.path.home="%OPENSEARCH_HOME%" -Dopensearch.path.conf="%OPENSEARCH_PATH_CONF%" ^ -Dopensearch.distribution.type="%OPENSEARCH_DISTRIBUTION_TYPE%" ^ -Dopensearch.bundled_jdk="%OPENSEARCH_BUNDLED_JDK%" ^ From 3ef0046916e93b5c1c7bde081eba4e672d1cec2e Mon Sep 17 00:00:00 2001 From: Marc Handalian Date: Wed, 7 Sep 2022 11:26:16 -0700 Subject: [PATCH 111/187] [Segment Replication] - Update replicas to commit SegmentInfos instead of relying on segments_N from primary shards. (#4402) * Segment Replication - Update replicas to commit SegmentInfos instead of relying on segments_N from primary shards. This change updates replicas to commit SegmentInfos before the shard is closed, on receiving a new commit point from a primary, and when a new primary is detected. This change also makes the public commitSegmentInfos on NRTEngine obsolete, refactoring IndexShard to simply call reset on the engine. Signed-off-by: Marc Handalian * Remove noise & extra log statement. Signed-off-by: Marc Handalian * PR feedback. Signed-off-by: Marc Handalian Signed-off-by: Marc Handalian --- CHANGELOG.md | 1 + .../index/engine/NRTReplicationEngine.java | 39 ++-- .../engine/NRTReplicationReaderManager.java | 3 + .../opensearch/index/shard/IndexShard.java | 28 +-- .../indices/recovery/MultiFileWriter.java | 5 +- .../engine/NRTReplicationEngineTests.java | 170 ++++++++--------- .../SegmentReplicationIndexShardTests.java | 174 +++++++++++++++++- .../indices/recovery/RecoveryTests.java | 3 +- .../index/engine/EngineTestCase.java | 8 +- .../index/shard/IndexShardTestCase.java | 10 +- 10 files changed, 288 insertions(+), 153 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 680f30eca0f20..bf5e1a70a493a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Use RemoteSegmentStoreDirectory instead of RemoteDirectory ([#4240](https://github.com/opensearch-project/OpenSearch/pull/4240)) - Plugin ZIP publication groupId value is configurable ([#4156](https://github.com/opensearch-project/OpenSearch/pull/4156)) - Add index specific setting for remote repository ([#4253](https://github.com/opensearch-project/OpenSearch/pull/4253)) +- [Segment Replication] Update replicas to commit SegmentInfos instead of relying on SIS files from primary shards. ([#4402](https://github.com/opensearch-project/OpenSearch/pull/4402)) ### Deprecated diff --git a/server/src/main/java/org/opensearch/index/engine/NRTReplicationEngine.java b/server/src/main/java/org/opensearch/index/engine/NRTReplicationEngine.java index cf753e3360c39..12d420aa245fa 100644 --- a/server/src/main/java/org/opensearch/index/engine/NRTReplicationEngine.java +++ b/server/src/main/java/org/opensearch/index/engine/NRTReplicationEngine.java @@ -54,6 +54,8 @@ public class NRTReplicationEngine extends Engine { private final LocalCheckpointTracker localCheckpointTracker; private final WriteOnlyTranslogManager translogManager; + private volatile long lastReceivedGen = SequenceNumbers.NO_OPS_PERFORMED; + private static final int SI_COUNTER_INCREMENT = 10; public NRTReplicationEngine(EngineConfig engineConfig) { @@ -120,14 +122,16 @@ public TranslogManager translogManager() { public synchronized void updateSegments(final SegmentInfos infos, long seqNo) throws IOException { // Update the current infos reference on the Engine's reader. + final long incomingGeneration = infos.getGeneration(); readerManager.updateSegments(infos); - // only update the persistedSeqNo and "lastCommitted" infos reference if the incoming segments have a higher - // generation. We can still refresh with incoming SegmentInfos that are not part of a commit point. - if (infos.getGeneration() > lastCommittedSegmentInfos.getGeneration()) { - this.lastCommittedSegmentInfos = infos; + // Commit and roll the xlog when we receive a different generation than what was last received. + // lower/higher gens are possible from a new primary that was just elected. + if (incomingGeneration != lastReceivedGen) { + commitSegmentInfos(); translogManager.rollTranslogGeneration(); } + lastReceivedGen = incomingGeneration; localCheckpointTracker.fastForwardProcessedSeqNo(seqNo); } @@ -141,20 +145,16 @@ public synchronized void updateSegments(final SegmentInfos infos, long seqNo) th * * @throws IOException - When there is an IO error committing the SegmentInfos. */ - public void commitSegmentInfos() throws IOException { - // TODO: This method should wait for replication events to finalize. - final SegmentInfos latestSegmentInfos = getLatestSegmentInfos(); - /* - This is a workaround solution which decreases the chances of conflict on replica nodes when same file is copied - from two different primaries during failover. Increasing counter helps in avoiding this conflict as counter is - used to generate new segment file names. The ideal solution is to identify the counter from previous primary. - */ - latestSegmentInfos.counter = latestSegmentInfos.counter + SI_COUNTER_INCREMENT; - latestSegmentInfos.changed(); - store.commitSegmentInfos(latestSegmentInfos, localCheckpointTracker.getMaxSeqNo(), localCheckpointTracker.getProcessedCheckpoint()); + private void commitSegmentInfos(SegmentInfos infos) throws IOException { + store.commitSegmentInfos(infos, localCheckpointTracker.getMaxSeqNo(), localCheckpointTracker.getProcessedCheckpoint()); + this.lastCommittedSegmentInfos = store.readLastCommittedSegmentsInfo(); translogManager.syncTranslog(); } + protected void commitSegmentInfos() throws IOException { + commitSegmentInfos(getLatestSegmentInfos()); + } + @Override public String getHistoryUUID() { return loadHistoryUUID(lastCommittedSegmentInfos.userData); @@ -354,6 +354,15 @@ protected final void closeNoLock(String reason, CountDownLatch closedLatch) { assert rwl.isWriteLockedByCurrentThread() || failEngineLock.isHeldByCurrentThread() : "Either the write lock must be held or the engine must be currently be failing itself"; try { + final SegmentInfos latestSegmentInfos = getLatestSegmentInfos(); + /* + This is a workaround solution which decreases the chances of conflict on replica nodes when same file is copied + from two different primaries during failover. Increasing counter helps in avoiding this conflict as counter is + used to generate new segment file names. The ideal solution is to identify the counter from previous primary. + */ + latestSegmentInfos.counter = latestSegmentInfos.counter + SI_COUNTER_INCREMENT; + latestSegmentInfos.changed(); + commitSegmentInfos(latestSegmentInfos); IOUtils.close(readerManager, translogManager, store::decRef); } catch (Exception e) { logger.warn("failed to close engine", e); diff --git a/server/src/main/java/org/opensearch/index/engine/NRTReplicationReaderManager.java b/server/src/main/java/org/opensearch/index/engine/NRTReplicationReaderManager.java index 16e615672a26f..8fbb24720aedc 100644 --- a/server/src/main/java/org/opensearch/index/engine/NRTReplicationReaderManager.java +++ b/server/src/main/java/org/opensearch/index/engine/NRTReplicationReaderManager.java @@ -74,6 +74,9 @@ protected OpenSearchDirectoryReader refreshIfNeeded(OpenSearchDirectoryReader re * @throws IOException - When Refresh fails with an IOException. */ public synchronized void updateSegments(SegmentInfos infos) throws IOException { + // roll over the currentInfo's generation, this ensures the on-disk gen + // is always increased. + infos.updateGeneration(currentInfos); currentInfos = infos; maybeRefresh(); } diff --git a/server/src/main/java/org/opensearch/index/shard/IndexShard.java b/server/src/main/java/org/opensearch/index/shard/IndexShard.java index 670af1f1c6fd9..28dc0ad49d4ec 100644 --- a/server/src/main/java/org/opensearch/index/shard/IndexShard.java +++ b/server/src/main/java/org/opensearch/index/shard/IndexShard.java @@ -623,7 +623,7 @@ public void updateShardState( if (indexSettings.isSegRepEnabled()) { // this Shard's engine was read only, we need to update its engine before restoring local history from xlog. assert newRouting.primary() && currentRouting.primary() == false; - promoteNRTReplicaToPrimary(); + resetEngineToGlobalCheckpoint(); } replicationTracker.activatePrimaryMode(getLocalCheckpoint()); ensurePeerRecoveryRetentionLeasesExist(); @@ -3557,7 +3557,9 @@ private void innerAcquireReplicaOperationPermit( currentGlobalCheckpoint, maxSeqNo ); - if (currentGlobalCheckpoint < maxSeqNo) { + // With Segment Replication enabled, we never want to reset a replica's engine unless + // it is promoted to primary. + if (currentGlobalCheckpoint < maxSeqNo && indexSettings.isSegRepEnabled() == false) { resetEngineToGlobalCheckpoint(); } else { getEngine().translogManager().rollTranslogGeneration(); @@ -4120,26 +4122,4 @@ RetentionLeaseSyncer getRetentionLeaseSyncer() { public GatedCloseable getSegmentInfosSnapshot() { return getEngine().getSegmentInfosSnapshot(); } - - /** - * With segment replication enabled - prepare the shard's engine to be promoted as the new primary. - * - * If this shard is currently using a replication engine, this method: - * 1. Invokes {@link NRTReplicationEngine#commitSegmentInfos()} to ensure the engine can be reopened as writeable from the latest refresh point. - * InternalEngine opens its IndexWriter from an on-disk commit point, but this replica may have recently synced from a primary's refresh point, meaning it has documents searchable in its in-memory SegmentInfos - * that are not part of a commit point. This ensures that those documents are made part of a commit and do not need to be reindexed after promotion. - * 2. Invokes resetEngineToGlobalCheckpoint - This call performs the engine swap, opening up as a writeable engine and replays any operations in the xlog. The operations indexed from xlog here will be - * any ack'd writes that were not copied to this replica before promotion. - */ - private void promoteNRTReplicaToPrimary() { - assert shardRouting.primary() && indexSettings.isSegRepEnabled(); - getReplicationEngine().ifPresentOrElse(engine -> { - try { - engine.commitSegmentInfos(); - resetEngineToGlobalCheckpoint(); - } catch (IOException e) { - throw new EngineException(shardId, "Unable to update replica to writeable engine, failing shard", e); - } - }, () -> { throw new EngineException(shardId, "Expected replica engine to be of type NRTReplicationEngine"); }); - } } diff --git a/server/src/main/java/org/opensearch/indices/recovery/MultiFileWriter.java b/server/src/main/java/org/opensearch/indices/recovery/MultiFileWriter.java index 3509615052707..ec3986017afac 100644 --- a/server/src/main/java/org/opensearch/indices/recovery/MultiFileWriter.java +++ b/server/src/main/java/org/opensearch/indices/recovery/MultiFileWriter.java @@ -156,7 +156,10 @@ private void innerWriteFileChunk(StoreFileMetadata fileMetadata, long position, + temporaryFileName + "] in " + Arrays.toString(store.directory().listAll()); - store.directory().sync(Collections.singleton(temporaryFileName)); + // With Segment Replication, we will fsync after a full commit has been received. + if (store.indexSettings().isSegRepEnabled() == false) { + store.directory().sync(Collections.singleton(temporaryFileName)); + } IndexOutput remove = removeOpenIndexOutputs(name); assert remove == null || remove == indexOutput; // remove maybe null if we got finished } diff --git a/server/src/test/java/org/opensearch/index/engine/NRTReplicationEngineTests.java b/server/src/test/java/org/opensearch/index/engine/NRTReplicationEngineTests.java index 540054782133a..96d5573621683 100644 --- a/server/src/test/java/org/opensearch/index/engine/NRTReplicationEngineTests.java +++ b/server/src/test/java/org/opensearch/index/engine/NRTReplicationEngineTests.java @@ -11,14 +11,11 @@ import org.apache.lucene.index.IndexCommit; import org.apache.lucene.index.NoMergePolicy; import org.apache.lucene.index.SegmentInfos; -import org.hamcrest.MatcherAssert; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.concurrent.GatedCloseable; import org.opensearch.common.lucene.Lucene; -import org.opensearch.common.lucene.search.Queries; import org.opensearch.common.settings.Settings; import org.opensearch.index.IndexSettings; -import org.opensearch.index.mapper.ParsedDocument; import org.opensearch.index.seqno.LocalCheckpointTracker; import org.opensearch.index.seqno.SequenceNumbers; import org.opensearch.index.store.Store; @@ -36,17 +33,21 @@ import java.util.stream.Collectors; import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.notNullValue; import static org.opensearch.index.seqno.SequenceNumbers.NO_OPS_PERFORMED; import static org.opensearch.index.seqno.SequenceNumbers.LOCAL_CHECKPOINT_KEY; import static org.opensearch.index.seqno.SequenceNumbers.MAX_SEQ_NO; public class NRTReplicationEngineTests extends EngineTestCase { + private static final IndexSettings INDEX_SETTINGS = IndexSettingsModule.newIndexSettings( + "index", + Settings.builder().put(IndexMetadata.SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT).build() + ); + public void testCreateEngine() throws IOException { final AtomicLong globalCheckpoint = new AtomicLong(SequenceNumbers.NO_OPS_PERFORMED); try ( - final Store nrtEngineStore = createStore(); + final Store nrtEngineStore = createStore(INDEX_SETTINGS, newDirectory()); final NRTReplicationEngine nrtEngine = buildNrtReplicaEngine(globalCheckpoint, nrtEngineStore) ) { final SegmentInfos latestSegmentInfos = nrtEngine.getLatestSegmentInfos(); @@ -70,7 +71,7 @@ public void testEngineWritesOpsToTranslog() throws Exception { final AtomicLong globalCheckpoint = new AtomicLong(SequenceNumbers.NO_OPS_PERFORMED); try ( - final Store nrtEngineStore = createStore(); + final Store nrtEngineStore = createStore(INDEX_SETTINGS, newDirectory()); final NRTReplicationEngine nrtEngine = buildNrtReplicaEngine(globalCheckpoint, nrtEngineStore) ) { List operations = generateHistoryOnReplica( @@ -93,6 +94,9 @@ public void testEngineWritesOpsToTranslog() throws Exception { // we don't index into nrtEngine, so get the doc ids from the regular engine. final List docs = getDocIds(engine, true); + // close the NRTEngine, it will commit on close and we'll reuse its store for an IE. + nrtEngine.close(); + // recover a new engine from the nrtEngine's xlog. nrtEngine.translogManager().syncTranslog(); try (InternalEngine engine = new InternalEngine(nrtEngine.config())) { @@ -104,88 +108,77 @@ public void testEngineWritesOpsToTranslog() throws Exception { } } - public void testUpdateSegments() throws Exception { + public void testUpdateSegments_replicaReceivesSISWithHigherGen() throws IOException { final AtomicLong globalCheckpoint = new AtomicLong(SequenceNumbers.NO_OPS_PERFORMED); try ( - final Store nrtEngineStore = createStore(); + final Store nrtEngineStore = createStore(INDEX_SETTINGS, newDirectory()); final NRTReplicationEngine nrtEngine = buildNrtReplicaEngine(globalCheckpoint, nrtEngineStore) ) { - // add docs to the primary engine. - List operations = generateHistoryOnReplica( - between(1, 500), - randomBoolean(), - randomBoolean(), - randomBoolean(), - Engine.Operation.TYPE.INDEX - ); - - for (Engine.Operation op : operations) { - applyOperation(engine, op); - applyOperation(nrtEngine, op); - } - - engine.refresh("test"); - - final SegmentInfos latestPrimaryInfos = engine.getLatestSegmentInfos(); - nrtEngine.updateSegments(latestPrimaryInfos, engine.getProcessedLocalCheckpoint()); - assertMatchingSegmentsAndCheckpoints(nrtEngine, latestPrimaryInfos); - - // assert a doc from the operations exists. - final ParsedDocument parsedDoc = createParsedDoc(operations.stream().findFirst().get().id(), null); - try (Engine.GetResult getResult = engine.get(newGet(true, parsedDoc), engine::acquireSearcher)) { - assertThat(getResult.exists(), equalTo(true)); - assertThat(getResult.docIdAndVersion(), notNullValue()); - } - - try (Engine.GetResult getResult = nrtEngine.get(newGet(true, parsedDoc), nrtEngine::acquireSearcher)) { - assertThat(getResult.exists(), equalTo(true)); - assertThat(getResult.docIdAndVersion(), notNullValue()); - } - - // Flush the primary and update the NRTEngine with the latest committed infos. - engine.flush(); - nrtEngine.translogManager().syncTranslog(); // to advance persisted checkpoint + // assume we start at the same gen. + assertEquals(2, nrtEngine.getLatestSegmentInfos().getGeneration()); + assertEquals(nrtEngine.getLatestSegmentInfos().getGeneration(), nrtEngine.getLastCommittedSegmentInfos().getGeneration()); + assertEquals(engine.getLatestSegmentInfos().getGeneration(), nrtEngine.getLatestSegmentInfos().getGeneration()); + + // flush the primary engine - we don't need any segments, just force a new commit point. + engine.flush(true, true); + assertEquals(3, engine.getLatestSegmentInfos().getGeneration()); + nrtEngine.updateSegments(engine.getLatestSegmentInfos(), engine.getProcessedLocalCheckpoint()); + assertEquals(3, nrtEngine.getLastCommittedSegmentInfos().getGeneration()); + assertEquals(3, nrtEngine.getLatestSegmentInfos().getGeneration()); + } + } - Set seqNos = operations.stream().map(Engine.Operation::seqNo).collect(Collectors.toSet()); + public void testUpdateSegments_replicaReceivesSISWithLowerGen() throws IOException { + // if the replica is already at segments_N that is received, it will commit segments_N+1. + final AtomicLong globalCheckpoint = new AtomicLong(SequenceNumbers.NO_OPS_PERFORMED); - nrtEngine.ensureOpen(); - try ( - Translog.Snapshot snapshot = assertAndGetInternalTranslogManager(nrtEngine.translogManager()).getTranslog().newSnapshot() - ) { - assertThat(snapshot.totalOperations(), equalTo(operations.size())); - assertThat( - TestTranslog.drainSnapshot(snapshot, false).stream().map(Translog.Operation::seqNo).collect(Collectors.toSet()), - equalTo(seqNos) - ); - } + try ( + final Store nrtEngineStore = createStore(INDEX_SETTINGS, newDirectory()); + final NRTReplicationEngine nrtEngine = buildNrtReplicaEngine(globalCheckpoint, nrtEngineStore) + ) { + nrtEngine.getLatestSegmentInfos().changed(); + nrtEngine.getLatestSegmentInfos().changed(); + // commit the infos to push us to segments_3. + nrtEngine.commitSegmentInfos(); + assertEquals(3, nrtEngine.getLastCommittedSegmentInfos().getGeneration()); + assertEquals(3, nrtEngine.getLatestSegmentInfos().getGeneration()); - final SegmentInfos primaryInfos = engine.getLastCommittedSegmentInfos(); + // update the replica with segments_2 from the primary. + final SegmentInfos primaryInfos = engine.getLatestSegmentInfos(); + assertEquals(2, primaryInfos.getGeneration()); nrtEngine.updateSegments(primaryInfos, engine.getProcessedLocalCheckpoint()); - assertMatchingSegmentsAndCheckpoints(nrtEngine, primaryInfos); + assertEquals(4, nrtEngine.getLastCommittedSegmentInfos().getGeneration()); + assertEquals(4, nrtEngine.getLatestSegmentInfos().getGeneration()); + assertEquals(primaryInfos.getVersion(), nrtEngine.getLatestSegmentInfos().getVersion()); + assertEquals(primaryInfos.getVersion(), nrtEngine.getLastCommittedSegmentInfos().getVersion()); - assertEquals( - assertAndGetInternalTranslogManager(nrtEngine.translogManager()).getTranslog().getGeneration().translogFileGeneration, - assertAndGetInternalTranslogManager(engine.translogManager()).getTranslog().getGeneration().translogFileGeneration - ); + nrtEngine.close(); + assertEquals(5, nrtEngine.getLastCommittedSegmentInfos().getGeneration()); + } + } - try ( - Translog.Snapshot snapshot = assertAndGetInternalTranslogManager(nrtEngine.translogManager()).getTranslog().newSnapshot() - ) { - assertThat(snapshot.totalOperations(), equalTo(operations.size())); - assertThat( - TestTranslog.drainSnapshot(snapshot, false).stream().map(Translog.Operation::seqNo).collect(Collectors.toSet()), - equalTo(seqNos) - ); - } + public void testUpdateSegments_replicaCommitsFirstReceivedInfos() throws IOException { + final AtomicLong globalCheckpoint = new AtomicLong(SequenceNumbers.NO_OPS_PERFORMED); - // Ensure the same hit count between engines. - int expectedDocCount; - try (final Engine.Searcher test = engine.acquireSearcher("test")) { - expectedDocCount = test.count(Queries.newMatchAllQuery()); - assertSearcherHits(nrtEngine, expectedDocCount); - } - assertEngineCleanedUp(nrtEngine, assertAndGetInternalTranslogManager(nrtEngine.translogManager()).getDeletionPolicy()); + try ( + final Store nrtEngineStore = createStore(INDEX_SETTINGS, newDirectory()); + final NRTReplicationEngine nrtEngine = buildNrtReplicaEngine(globalCheckpoint, nrtEngineStore) + ) { + assertEquals(2, nrtEngine.getLastCommittedSegmentInfos().getGeneration()); + assertEquals(2, nrtEngine.getLatestSegmentInfos().getGeneration()); + // bump the latest infos version a couple of times so that we can assert the correct version after commit. + engine.getLatestSegmentInfos().changed(); + engine.getLatestSegmentInfos().changed(); + assertNotEquals(nrtEngine.getLatestSegmentInfos().getVersion(), engine.getLatestSegmentInfos().getVersion()); + + // update replica with the latest primary infos, it will be the same gen, segments_2, ensure it is also committed. + final SegmentInfos primaryInfos = engine.getLatestSegmentInfos(); + assertEquals(2, primaryInfos.getGeneration()); + nrtEngine.updateSegments(primaryInfos, engine.getProcessedLocalCheckpoint()); + final SegmentInfos lastCommittedSegmentInfos = nrtEngine.getLastCommittedSegmentInfos(); + assertEquals(primaryInfos.getVersion(), nrtEngine.getLatestSegmentInfos().getVersion()); + assertEquals(primaryInfos.getVersion(), lastCommittedSegmentInfos.getVersion()); } } @@ -193,7 +186,7 @@ public void testTrimTranslogOps() throws Exception { final AtomicLong globalCheckpoint = new AtomicLong(SequenceNumbers.NO_OPS_PERFORMED); try ( - final Store nrtEngineStore = createStore(); + final Store nrtEngineStore = createStore(INDEX_SETTINGS, newDirectory()); final NRTReplicationEngine nrtEngine = buildNrtReplicaEngine(globalCheckpoint, nrtEngineStore); ) { List operations = generateHistoryOnReplica( @@ -227,12 +220,9 @@ public void testCommitSegmentInfos() throws Exception { // This test asserts that NRTReplication#commitSegmentInfos creates a new commit point with the latest checkpoints // stored in user data. final AtomicLong globalCheckpoint = new AtomicLong(SequenceNumbers.NO_OPS_PERFORMED); - final IndexSettings indexSettings = IndexSettingsModule.newIndexSettings( - "index", - Settings.builder().put(IndexMetadata.SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT).build() - ); + try ( - final Store nrtEngineStore = createStore(indexSettings, newDirectory()); + final Store nrtEngineStore = createStore(INDEX_SETTINGS, newDirectory()); final NRTReplicationEngine nrtEngine = buildNrtReplicaEngine(globalCheckpoint, nrtEngineStore) ) { List operations = generateHistoryOnReplica(between(1, 500), randomBoolean(), randomBoolean(), randomBoolean()) @@ -268,22 +258,6 @@ public void testCommitSegmentInfos() throws Exception { } } - private void assertMatchingSegmentsAndCheckpoints(NRTReplicationEngine nrtEngine, SegmentInfos expectedSegmentInfos) - throws IOException { - assertEquals(engine.getPersistedLocalCheckpoint(), nrtEngine.getPersistedLocalCheckpoint()); - assertEquals(engine.getProcessedLocalCheckpoint(), nrtEngine.getProcessedLocalCheckpoint()); - assertEquals(engine.getLocalCheckpointTracker().getMaxSeqNo(), nrtEngine.getLocalCheckpointTracker().getMaxSeqNo()); - assertEquals(expectedSegmentInfos.files(true), nrtEngine.getLatestSegmentInfos().files(true)); - assertEquals(expectedSegmentInfos.getUserData(), nrtEngine.getLatestSegmentInfos().getUserData()); - assertEquals(expectedSegmentInfos.getVersion(), nrtEngine.getLatestSegmentInfos().getVersion()); - } - - private void assertSearcherHits(Engine engine, int hits) { - try (final Engine.Searcher test = engine.acquireSearcher("test")) { - MatcherAssert.assertThat(test, EngineSearcherTotalHitsMatcher.engineSearcherTotalHits(hits)); - } - } - private NRTReplicationEngine buildNrtReplicaEngine(AtomicLong globalCheckpoint, Store store) throws IOException { Lucene.cleanLuceneIndex(store.directory()); final Path translogDir = createTempDir(); diff --git a/server/src/test/java/org/opensearch/index/shard/SegmentReplicationIndexShardTests.java b/server/src/test/java/org/opensearch/index/shard/SegmentReplicationIndexShardTests.java index 3af882a8087ec..007317f6e71cd 100644 --- a/server/src/test/java/org/opensearch/index/shard/SegmentReplicationIndexShardTests.java +++ b/server/src/test/java/org/opensearch/index/shard/SegmentReplicationIndexShardTests.java @@ -8,6 +8,8 @@ package org.opensearch.index.shard; +import org.apache.lucene.index.IndexCommit; +import org.apache.lucene.index.SegmentInfos; import org.junit.Assert; import org.opensearch.OpenSearchException; import org.opensearch.action.ActionListener; @@ -15,6 +17,7 @@ import org.opensearch.action.index.IndexRequest; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.routing.ShardRouting; +import org.opensearch.common.concurrent.GatedCloseable; import org.opensearch.common.lease.Releasable; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Settings; @@ -48,6 +51,7 @@ import java.io.IOException; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -66,7 +70,7 @@ public class SegmentReplicationIndexShardTests extends OpenSearchIndexLevelRepli .build(); /** - * Test that latestReplicationCheckpoint returns null only for docrep enabled indices + * Test that latestReplicationCheckpoint returns null only for docrep enabled indices */ public void testReplicationCheckpointNullForDocRep() throws IOException { Settings indexSettings = Settings.builder().put(IndexMetadata.SETTING_REPLICATION_TYPE, "DOCUMENT").put(Settings.EMPTY).build(); @@ -76,11 +80,10 @@ public void testReplicationCheckpointNullForDocRep() throws IOException { } /** - * Test that latestReplicationCheckpoint returns ReplicationCheckpoint for segrep enabled indices + * Test that latestReplicationCheckpoint returns ReplicationCheckpoint for segrep enabled indices */ - public void testReplicationCheckpointNotNullForSegReb() throws IOException { - Settings indexSettings = Settings.builder().put(IndexMetadata.SETTING_REPLICATION_TYPE, "SEGMENT").put(Settings.EMPTY).build(); - final IndexShard indexShard = newStartedShard(indexSettings); + public void testReplicationCheckpointNotNullForSegRep() throws IOException { + final IndexShard indexShard = newStartedShard(randomBoolean(), settings, new NRTReplicationEngineFactory()); final ReplicationCheckpoint replicationCheckpoint = indexShard.getLatestReplicationCheckpoint(); assertNotNull(replicationCheckpoint); closeShards(indexShard); @@ -205,6 +208,132 @@ public void testPublishCheckpointAfterRelocationHandOff() throws IOException { closeShards(shard); } + public void testReplicaReceivesGenIncrease() throws Exception { + try (ReplicationGroup shards = createGroup(1, settings, new NRTReplicationEngineFactory())) { + shards.startAll(); + final IndexShard primary = shards.getPrimary(); + final IndexShard replica = shards.getReplicas().get(0); + final int numDocs = randomIntBetween(10, 100); + shards.indexDocs(numDocs); + flushShard(primary, true); + replicateSegments(primary, shards.getReplicas()); + + final int totalDocs = numDocs + shards.indexDocs(randomIntBetween(numDocs + 1, numDocs + 10)); + flushShard(primary); + replicateSegments(primary, shards.getReplicas()); + + assertEqualCommittedSegments(primary, replica); + assertDocCount(primary, totalDocs); + assertDocCount(replica, totalDocs); + } + } + + public void testReplicaReceivesLowerGeneration() throws Exception { + // when a replica gets incoming segments that are lower than what it currently has on disk. + + // start 3 nodes Gens: P [2], R [2], R[2] + // index some docs and flush twice, push to only 1 replica. + // State Gens: P [4], R-1 [3], R-2 [2] + // Promote R-2 as the new primary and demote the old primary. + // State Gens: R[4], R-1 [3], P [4] - *commit on close of NRTEngine, xlog replayed and commit made. + // index docs on new primary and flush + // replicate to all. + // Expected result: State Gens: P[4], R-1 [4], R-2 [4] + try (ReplicationGroup shards = createGroup(2, settings, new NRTReplicationEngineFactory())) { + shards.startAll(); + final IndexShard primary = shards.getPrimary(); + final IndexShard replica_1 = shards.getReplicas().get(0); + final IndexShard replica_2 = shards.getReplicas().get(1); + int numDocs = randomIntBetween(10, 100); + shards.indexDocs(numDocs); + flushShard(primary, false); + replicateSegments(primary, List.of(replica_1)); + numDocs = randomIntBetween(numDocs + 1, numDocs + 10); + shards.indexDocs(numDocs); + flushShard(primary, false); + assertLatestCommitGen(4, primary); + replicateSegments(primary, List.of(replica_1)); + + assertEqualCommittedSegments(primary, replica_1); + assertLatestCommitGen(4, primary, replica_1); + assertLatestCommitGen(2, replica_2); + + shards.promoteReplicaToPrimary(replica_2).get(); + primary.close("demoted", false); + primary.store().close(); + IndexShard oldPrimary = shards.addReplicaWithExistingPath(primary.shardPath(), primary.routingEntry().currentNodeId()); + shards.recoverReplica(oldPrimary); + assertLatestCommitGen(4, oldPrimary); + assertEqualCommittedSegments(oldPrimary, replica_1); + + assertLatestCommitGen(4, replica_2); + + numDocs = randomIntBetween(numDocs + 1, numDocs + 10); + shards.indexDocs(numDocs); + flushShard(replica_2, false); + replicateSegments(replica_2, shards.getReplicas()); + assertEqualCommittedSegments(replica_2, oldPrimary, replica_1); + } + } + + public void testReplicaRestarts() throws Exception { + try (ReplicationGroup shards = createGroup(3, settings, new NRTReplicationEngineFactory())) { + shards.startAll(); + IndexShard primary = shards.getPrimary(); + // 1. Create ops that are in the index and xlog of both shards but not yet part of a commit point. + final int numDocs = shards.indexDocs(randomInt(10)); + + // refresh and copy the segments over. + if (randomBoolean()) { + flushShard(primary); + } + primary.refresh("Test"); + replicateSegments(primary, shards.getReplicas()); + + // at this point both shards should have numDocs persisted and searchable. + assertDocCounts(primary, numDocs, numDocs); + for (IndexShard shard : shards.getReplicas()) { + assertDocCounts(shard, numDocs, numDocs); + } + + final int i1 = randomInt(5); + for (int i = 0; i < i1; i++) { + shards.indexDocs(randomInt(10)); + + // randomly resetart a replica + final IndexShard replicaToRestart = getRandomReplica(shards); + replicaToRestart.close("restart", false); + replicaToRestart.store().close(); + shards.removeReplica(replicaToRestart); + final IndexShard newReplica = shards.addReplicaWithExistingPath( + replicaToRestart.shardPath(), + replicaToRestart.routingEntry().currentNodeId() + ); + shards.recoverReplica(newReplica); + + // refresh and push segments to our other replicas. + if (randomBoolean()) { + failAndPromoteRandomReplica(shards); + } + flushShard(shards.getPrimary()); + replicateSegments(shards.getPrimary(), shards.getReplicas()); + } + primary = shards.getPrimary(); + + // refresh and push segments to our other replica. + flushShard(primary); + replicateSegments(primary, shards.getReplicas()); + + for (IndexShard shard : shards) { + assertConsistentHistoryBetweenTranslogAndLucene(shard); + } + final List docsAfterReplication = getDocIdAndSeqNos(shards.getPrimary()); + for (IndexShard shard : shards.getReplicas()) { + assertThat(shard.routingEntry().toString(), getDocIdAndSeqNos(shard), equalTo(docsAfterReplication)); + } + } + } + public void testNRTReplicaPromotedAsPrimary() throws Exception { try (ReplicationGroup shards = createGroup(2, settings, new NRTReplicationEngineFactory())) { shards.startAll(); @@ -523,4 +652,39 @@ public void onReplicationFailure(SegmentReplicationState state, OpenSearchExcept assertEquals("Should have resolved listener with failure", 0, latch.getCount()); assertNull(targetService.get(target.getId())); } + + private IndexShard getRandomReplica(ReplicationGroup shards) { + return shards.getReplicas().get(randomInt(shards.getReplicas().size() - 1)); + } + + private IndexShard failAndPromoteRandomReplica(ReplicationGroup shards) throws IOException { + IndexShard primary = shards.getPrimary(); + final IndexShard newPrimary = getRandomReplica(shards); + shards.promoteReplicaToPrimary(newPrimary); + primary.close("demoted", true); + primary.store().close(); + primary = shards.addReplicaWithExistingPath(primary.shardPath(), primary.routingEntry().currentNodeId()); + shards.recoverReplica(primary); + return newPrimary; + } + + private void assertLatestCommitGen(long expected, IndexShard... shards) throws IOException { + for (IndexShard indexShard : shards) { + try (final GatedCloseable commit = indexShard.acquireLastIndexCommit(false)) { + assertEquals(expected, commit.get().getGeneration()); + } + } + } + + private void assertEqualCommittedSegments(IndexShard primary, IndexShard... replicas) throws IOException { + for (IndexShard replica : replicas) { + final SegmentInfos replicaInfos = replica.store().readLastCommittedSegmentsInfo(); + final SegmentInfos primaryInfos = primary.store().readLastCommittedSegmentsInfo(); + final Map latestReplicaMetadata = replica.store().getSegmentMetadataMap(replicaInfos); + final Map latestPrimaryMetadata = primary.store().getSegmentMetadataMap(primaryInfos); + final Store.RecoveryDiff diff = Store.segmentReplicationDiff(latestPrimaryMetadata, latestReplicaMetadata); + assertTrue(diff.different.isEmpty()); + assertTrue(diff.missing.isEmpty()); + } + } } diff --git a/server/src/test/java/org/opensearch/indices/recovery/RecoveryTests.java b/server/src/test/java/org/opensearch/indices/recovery/RecoveryTests.java index 3ea74dbf38919..cc5100fba9010 100644 --- a/server/src/test/java/org/opensearch/indices/recovery/RecoveryTests.java +++ b/server/src/test/java/org/opensearch/indices/recovery/RecoveryTests.java @@ -61,6 +61,7 @@ import org.opensearch.index.engine.EngineFactory; import org.opensearch.index.engine.InternalEngineFactory; import org.opensearch.index.engine.InternalEngineTests; +import org.opensearch.index.engine.NRTReplicationEngineFactory; import org.opensearch.index.mapper.SourceToParse; import org.opensearch.index.replication.OpenSearchIndexLevelReplicationTestCase; import org.opensearch.index.replication.RecoveryDuringReplicationTests; @@ -106,7 +107,7 @@ public void testTranslogHistoryTransferred() throws Exception { public void testWithSegmentReplication_ReplicaUsesPrimaryTranslogUUID() throws Exception { Settings settings = Settings.builder().put(IndexMetadata.SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT).build(); - try (ReplicationGroup shards = createGroup(2, settings)) { + try (ReplicationGroup shards = createGroup(2, settings, new NRTReplicationEngineFactory())) { shards.startAll(); final String expectedUUID = getTranslog(shards.getPrimary()).getTranslogUUID(); assertTrue( diff --git a/test/framework/src/main/java/org/opensearch/index/engine/EngineTestCase.java b/test/framework/src/main/java/org/opensearch/index/engine/EngineTestCase.java index af754d77560cc..f4a9f51789679 100644 --- a/test/framework/src/main/java/org/opensearch/index/engine/EngineTestCase.java +++ b/test/framework/src/main/java/org/opensearch/index/engine/EngineTestCase.java @@ -1507,10 +1507,10 @@ public static MapperService createMapperService() throws IOException { * Exposes a translog associated with the given engine for testing purpose. */ public static Translog getTranslog(Engine engine) { - assert engine instanceof InternalEngine : "only InternalEngines have translogs, got: " + engine.getClass(); - InternalEngine internalEngine = (InternalEngine) engine; - internalEngine.ensureOpen(); - TranslogManager translogManager = internalEngine.translogManager(); + assert engine instanceof InternalEngine || engine instanceof NRTReplicationEngine + : "only InternalEngines or NRTReplicationEngines have translogs, got: " + engine.getClass(); + engine.ensureOpen(); + TranslogManager translogManager = engine.translogManager(); assert translogManager instanceof InternalTranslogManager : "only InternalTranslogManager have translogs, got: " + engine.getClass(); InternalTranslogManager internalTranslogManager = (InternalTranslogManager) translogManager; diff --git a/test/framework/src/main/java/org/opensearch/index/shard/IndexShardTestCase.java b/test/framework/src/main/java/org/opensearch/index/shard/IndexShardTestCase.java index 073dc4b84472e..09eca006d600a 100644 --- a/test/framework/src/main/java/org/opensearch/index/shard/IndexShardTestCase.java +++ b/test/framework/src/main/java/org/opensearch/index/shard/IndexShardTestCase.java @@ -139,6 +139,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -1252,10 +1253,10 @@ public final List replicateSegments( List replicaShards ) throws IOException, InterruptedException { final CountDownLatch countDownLatch = new CountDownLatch(replicaShards.size()); - Store.MetadataSnapshot primaryMetadata; + Map primaryMetadata; try (final GatedCloseable segmentInfosSnapshot = primaryShard.getSegmentInfosSnapshot()) { final SegmentInfos primarySegmentInfos = segmentInfosSnapshot.get(); - primaryMetadata = primaryShard.store().getMetadata(primarySegmentInfos); + primaryMetadata = primaryShard.store().getSegmentMetadataMap(primarySegmentInfos); } List ids = new ArrayList<>(); for (IndexShard replica : replicaShards) { @@ -1267,12 +1268,11 @@ public final List replicateSegments( public void onReplicationDone(SegmentReplicationState state) { try (final GatedCloseable snapshot = replica.getSegmentInfosSnapshot()) { final SegmentInfos replicaInfos = snapshot.get(); - final Store.MetadataSnapshot replicaMetadata = replica.store().getMetadata(replicaInfos); - final Store.RecoveryDiff recoveryDiff = primaryMetadata.recoveryDiff(replicaMetadata); + final Map replicaMetadata = replica.store().getSegmentMetadataMap(replicaInfos); + final Store.RecoveryDiff recoveryDiff = Store.segmentReplicationDiff(primaryMetadata, replicaMetadata); assertTrue(recoveryDiff.missing.isEmpty()); assertTrue(recoveryDiff.different.isEmpty()); assertEquals(recoveryDiff.identical.size(), primaryMetadata.size()); - assertEquals(primaryMetadata.getCommitUserData(), replicaMetadata.getCommitUserData()); } catch (Exception e) { throw ExceptionsHelper.convertToRuntime(e); } finally { From dac99d543b50055ccd8354fd9f93eb4efd9e359d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 Sep 2022 14:53:25 -0400 Subject: [PATCH 112/187] Bump reactor-netty-core from 1.0.19 to 1.0.22 in /plugins/repository-azure (#4447) * Bump reactor-netty-core in /plugins/repository-azure Bumps [reactor-netty-core](https://github.com/reactor/reactor-netty) from 1.0.19 to 1.0.22. - [Release notes](https://github.com/reactor/reactor-netty/releases) - [Commits](https://github.com/reactor/reactor-netty/compare/v1.0.19...v1.0.22) --- updated-dependencies: - dependency-name: io.projectreactor.netty:reactor-netty-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * Updating SHAs Signed-off-by: dependabot[bot] * Update changelog Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: dependabot[bot] --- CHANGELOG.md | 3 ++- plugins/repository-azure/build.gradle | 2 +- .../licenses/reactor-netty-core-1.0.19.jar.sha1 | 1 - .../licenses/reactor-netty-core-1.0.22.jar.sha1 | 1 + 4 files changed, 4 insertions(+), 3 deletions(-) delete mode 100644 plugins/repository-azure/licenses/reactor-netty-core-1.0.19.jar.sha1 create mode 100644 plugins/repository-azure/licenses/reactor-netty-core-1.0.22.jar.sha1 diff --git a/CHANGELOG.md b/CHANGELOG.md index bf5e1a70a493a..ec8441831448f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Support for labels on version bump PRs, skip label support for changelog verifier ([#4391](https://github.com/opensearch-project/OpenSearch/pull/4391)) ### Dependencies - Bumps `org.gradle.test-retry` from 1.4.0 to 1.4.1 +- Bumps `reactor-netty-core` from 1.0.19 to 1.0.22 ### Dependencies - Bumps `com.diffplug.spotless` from 6.9.1 to 6.10.0 @@ -77,4 +78,4 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) [Unreleased]: https://github.com/opensearch-project/OpenSearch/compare/2.2.0...HEAD -[2.x]: https://github.com/opensearch-project/OpenSearch/compare/2.2.0...2.x +[2.x]: https://github.com/opensearch-project/OpenSearch/compare/2.2.0...2.x \ No newline at end of file diff --git a/plugins/repository-azure/build.gradle b/plugins/repository-azure/build.gradle index 1af2de4f176f2..08cd32e80a7ca 100644 --- a/plugins/repository-azure/build.gradle +++ b/plugins/repository-azure/build.gradle @@ -58,7 +58,7 @@ dependencies { api 'org.reactivestreams:reactive-streams:1.0.3' api 'io.projectreactor:reactor-core:3.4.18' api 'io.projectreactor.netty:reactor-netty:1.0.18' - api 'io.projectreactor.netty:reactor-netty-core:1.0.19' + api 'io.projectreactor.netty:reactor-netty-core:1.0.22' api 'io.projectreactor.netty:reactor-netty-http:1.0.18' api "org.slf4j:slf4j-api:${versions.slf4j}" api "com.fasterxml.jackson.core:jackson-annotations:${versions.jackson}" diff --git a/plugins/repository-azure/licenses/reactor-netty-core-1.0.19.jar.sha1 b/plugins/repository-azure/licenses/reactor-netty-core-1.0.19.jar.sha1 deleted file mode 100644 index 74df264a2b908..0000000000000 --- a/plugins/repository-azure/licenses/reactor-netty-core-1.0.19.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -adb58ba62d297b56d6b7915a50f048eddcfc81a6 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/reactor-netty-core-1.0.22.jar.sha1 b/plugins/repository-azure/licenses/reactor-netty-core-1.0.22.jar.sha1 new file mode 100644 index 0000000000000..4c82e37d27043 --- /dev/null +++ b/plugins/repository-azure/licenses/reactor-netty-core-1.0.22.jar.sha1 @@ -0,0 +1 @@ +5c2a258ac71e525c65f2e3a0bcf458b6c79bbc16 \ No newline at end of file From f15c9e9b57d0daeb3b139a37e24d45249a5c93f0 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 2 Aug 2022 11:45:51 +0530 Subject: [PATCH 113/187] Add Executor to decommission node attribute Signed-off-by: Rishab Nahata --- ...NodeAttributeClusterStateTaskExecutor.java | 141 ++++++++++++++ .../decommission/DecommissionAttribute.java | 108 +++++++++++ ...ttributeClusterStateTaskExecutorTests.java | 178 ++++++++++++++++++ 3 files changed, 427 insertions(+) create mode 100644 server/src/main/java/org/opensearch/cluster/coordination/DecommissionNodeAttributeClusterStateTaskExecutor.java create mode 100644 server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java create mode 100644 server/src/test/java/org/opensearch/cluster/coordination/DecommissionNodeAttributeClusterStateTaskExecutorTests.java diff --git a/server/src/main/java/org/opensearch/cluster/coordination/DecommissionNodeAttributeClusterStateTaskExecutor.java b/server/src/main/java/org/opensearch/cluster/coordination/DecommissionNodeAttributeClusterStateTaskExecutor.java new file mode 100644 index 0000000000000..d71cd98d5f25e --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/coordination/DecommissionNodeAttributeClusterStateTaskExecutor.java @@ -0,0 +1,141 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.coordination; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.message.ParameterizedMessage; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.ClusterStateTaskExecutor; +import org.opensearch.cluster.ClusterStateTaskListener; +import org.opensearch.cluster.decommission.DecommissionAttribute; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.node.DiscoveryNodes; +import org.opensearch.cluster.routing.allocation.AllocationService; +import org.opensearch.persistent.PersistentTasksCustomMetadata; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.function.Predicate; + +/** + * Decommissions and shuts down nodes having a given attribute and updates the cluster state + * + * @opensearch.internal + */ +public class DecommissionNodeAttributeClusterStateTaskExecutor + implements + ClusterStateTaskExecutor, + ClusterStateTaskListener { + + private final AllocationService allocationService; + private final Logger logger; + + /** + * Task for the executor. + * + * @opensearch.internal + */ + public static class Task { + + private final DecommissionAttribute decommissionAttribute; + private final String reason; + + public Task(final DecommissionAttribute decommissionAttribute, final String reason) { + this.decommissionAttribute = decommissionAttribute; + this.reason = reason; + } + + public DecommissionAttribute decommissionAttribute() { + return decommissionAttribute; + } + + public String reason() { + return reason; + } + + @Override + public String toString() { + return "Decommission Node Attribute Task{" + + "decommissionAttribute=" + + decommissionAttribute + + ", reason='" + + reason + + '\'' + + '}'; + } + } + + public DecommissionNodeAttributeClusterStateTaskExecutor(final AllocationService allocationService, final Logger logger) { + this.allocationService = allocationService; + this.logger = logger; + } + + @Override + public ClusterTasksResult execute(ClusterState currentState, List tasks) throws Exception { + final DiscoveryNodes.Builder remainingNodesBuilder = DiscoveryNodes.builder(currentState.nodes()); + List nodesToBeRemoved = new ArrayList(); + for (final Task task : tasks) { + final Predicate shouldRemoveNodePredicate = discoveryNode -> nodeHasDecommissionedAttribute(discoveryNode, task); + Iterator nodesIter = currentState.nodes().getNodes().valuesIt(); + while (nodesIter.hasNext()) { + final DiscoveryNode node = nodesIter.next(); + if (shouldRemoveNodePredicate.test(node) && currentState.nodes().nodeExists(node)) { + nodesToBeRemoved.add(node); + } + } + } + if (nodesToBeRemoved.size() <= 0) { + // no nodes to remove, will keep the current cluster state + return ClusterTasksResult.builder() + .successes(tasks) + .build(currentState); + } + for (DiscoveryNode nodeToBeRemoved : nodesToBeRemoved) { + remainingNodesBuilder.remove(nodeToBeRemoved); + } + + final ClusterState remainingNodesClusterState = remainingNodesClusterState(currentState, remainingNodesBuilder); + + return getTaskClusterTasksResult(currentState, tasks, remainingNodesClusterState); + } + + private boolean nodeHasDecommissionedAttribute(DiscoveryNode discoveryNode, Task task) { + String discoveryNodeAttributeValue = discoveryNode.getAttributes().get(task.decommissionAttribute().attributeName()); + return discoveryNodeAttributeValue != null && task.decommissionAttribute().attributeValues().contains(discoveryNodeAttributeValue); + } + + // visible for testing + // hook is used in testing to ensure that correct cluster state is used to test whether a + // rejoin or reroute is needed + protected ClusterState remainingNodesClusterState(final ClusterState currentState, DiscoveryNodes.Builder remainingNodesBuilder) { + return ClusterState.builder(currentState).nodes(remainingNodesBuilder).build(); + } + + protected ClusterTasksResult getTaskClusterTasksResult( + ClusterState currentState, + List tasks, + ClusterState remainingNodesClusterState + ) { + ClusterState ptasksDisassociatedState = PersistentTasksCustomMetadata.disassociateDeadNodes(remainingNodesClusterState); + final ClusterTasksResult.Builder resultBuilder = ClusterTasksResult.< + DecommissionNodeAttributeClusterStateTaskExecutor.Task>builder().successes(tasks); + return resultBuilder.build(allocationService.disassociateDeadNodes(ptasksDisassociatedState, true, describeTasks(tasks))); + } + + @Override + public void onFailure(final String source, final Exception e) { + logger.error(() -> new ParameterizedMessage("unexpected failure during [{}]", source), e); + } + + @Override + public void onNoLongerClusterManager(String source) { + logger.debug("no longer cluster-manager while decommissioning node attribute [{}]", source); + } +} diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java new file mode 100644 index 0000000000000..6260af2823687 --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java @@ -0,0 +1,108 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.decommission; + +import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.common.io.stream.Writeable; + +import java.io.IOException; +import java.util.List; +import java.util.Objects; + +public final class DecommissionAttribute implements Writeable { + private final String attributeName; + private final List attributeValues; + + /** + * Update the attribute values for a given attribute name to decommission + * + * @param decommissionAttribute current decommissioned attribute object + * @param attributeValues values to be updated with + */ + public DecommissionAttribute(DecommissionAttribute decommissionAttribute, List attributeValues) { + this(decommissionAttribute.attributeName, attributeValues); + } + + /** + * Constructs new decommission attribute name values pair + * + * @param attributeName attribute name + * @param attributeValues attribute values + */ + public DecommissionAttribute(String attributeName, List attributeValues) { + this.attributeName = attributeName; + this.attributeValues = attributeValues; + } + + /** + * Returns attribute name + * + * @return attributeName + */ + public String attributeName() { + return this.attributeName; + } + + /** + * Returns attribute values + * + * @return attributeValues + */ + public List attributeValues() { + return this.attributeValues; + } + + public DecommissionAttribute(StreamInput in) throws IOException { + attributeName = in.readString(); + attributeValues = in.readStringList(); + } + + /** + * Writes decommission attribute name values to stream output + * + * @param out stream output + */ + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeString(attributeName); + out.writeStringCollection(attributeValues); + } + + /** + * Checks if this instance is equal to the other instance in attributeName other than {@link #attributeValues}. + * + * @param other other decommission attribute name values + * @return {@code true} if both instances equal in attributeName fields but the attributeValues fields + */ + public boolean equalsIgnoreValues(DecommissionAttribute other) { + return attributeName.equals(other.attributeName); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + DecommissionAttribute that = (DecommissionAttribute) o; + + if (!attributeName.equals(that.attributeName)) return false; + return attributeValues.equals(that.attributeValues); + } + + @Override + public int hashCode() { + return Objects.hash(attributeName, attributeValues); + } + + @Override + public String toString() { + return "DecommissionAttribute{" + attributeName + "}{" + attributeValues().toString() + "}"; + } +} diff --git a/server/src/test/java/org/opensearch/cluster/coordination/DecommissionNodeAttributeClusterStateTaskExecutorTests.java b/server/src/test/java/org/opensearch/cluster/coordination/DecommissionNodeAttributeClusterStateTaskExecutorTests.java new file mode 100644 index 0000000000000..204d31f18e2cf --- /dev/null +++ b/server/src/test/java/org/opensearch/cluster/coordination/DecommissionNodeAttributeClusterStateTaskExecutorTests.java @@ -0,0 +1,178 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.coordination; + +import org.opensearch.Version; +import org.opensearch.cluster.ClusterName; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.ClusterStateTaskExecutor; +import org.opensearch.cluster.decommission.DecommissionAttribute; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.node.DiscoveryNodeRole; +import org.opensearch.cluster.node.DiscoveryNodes; +import org.opensearch.cluster.routing.allocation.AllocationService; +import org.opensearch.test.OpenSearchTestCase; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; + +import static java.util.Collections.singletonMap; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class DecommissionNodeAttributeClusterStateTaskExecutorTests extends OpenSearchTestCase { + + public void testRemoveNodesForDecommissionedAttribute() throws Exception { + final AllocationService allocationService = mock(AllocationService.class); + when(allocationService.disassociateDeadNodes(any(ClusterState.class), eq(true), any(String.class))).thenAnswer( + im -> im.getArguments()[0] + ); + final AtomicReference remainingNodesClusterState = new AtomicReference<>(); + ClusterState clusterState = ClusterState.builder(new ClusterName("test")).build(); + + logger.info("--> adding five nodes on same zone_1"); + clusterState = addNodes(clusterState, "zone_1", "node1", "node2", "node3", "node4", "node5"); + + logger.info("--> adding five nodes on same zone_2"); + clusterState = addNodes(clusterState, "zone_2", "node6", "node7", "node8", "node9", "node10"); + + logger.info("--> adding five nodes on same zone_3"); + clusterState = addNodes(clusterState, "zone_3", "node11", "node12", "node13", "node14", "node15"); + + final DecommissionNodeAttributeClusterStateTaskExecutor executor = new DecommissionNodeAttributeClusterStateTaskExecutor( + allocationService, + logger + ) { + @Override + protected ClusterState remainingNodesClusterState(ClusterState currentState, DiscoveryNodes.Builder remainingNodesBuilder) { + remainingNodesClusterState.set(super.remainingNodesClusterState(currentState, remainingNodesBuilder)); + return remainingNodesClusterState.get(); + } + }; + + final List tasks = new ArrayList<>(); + tasks.add( + new DecommissionNodeAttributeClusterStateTaskExecutor.Task( + new DecommissionAttribute("zone", Collections.singletonList("zone_3")), + "unit test zone decommission executor" + ) + ); + + final ClusterStateTaskExecutor.ClusterTasksResult result = executor.execute( + clusterState, + tasks + ); + + ClusterState expectedClusterState = remainingNodesClusterState.get(); + ClusterState actualClusterState = result.resultingState; + + // Assert cluster state is updated and is successful + verify(allocationService).disassociateDeadNodes(eq(expectedClusterState), eq(true), any(String.class)); + assertEquals(actualClusterState, expectedClusterState); + assertTrue(result.executionResults.get(tasks.get(0)).isSuccess()); + + // Verify only 10 nodes present in the cluster after decommissioning + assertEquals(actualClusterState.nodes().getNodes().size(), 10); + + // Verify no nodes has attribute (zone, zone_3) + Iterator currDiscoveryNodeIterator = actualClusterState.nodes().getNodes().valuesIt(); + while (currDiscoveryNodeIterator.hasNext()) { + final DiscoveryNode node = currDiscoveryNodeIterator.next(); + assertNotEquals(node.getAttributes().get("zone"), "zone_3"); + } + } + + public void testSameClusterStateAfterExecutionForUnknownAttributeNameAndValue() throws Exception { + final AllocationService allocationService = mock(AllocationService.class); + when(allocationService.disassociateDeadNodes(any(ClusterState.class), eq(true), any(String.class))).thenAnswer( + im -> im.getArguments()[0] + ); + final AtomicReference remainingNodesClusterState = new AtomicReference<>(); + ClusterState clusterState = ClusterState.builder(new ClusterName("test")).build(); + + logger.info("--> adding five nodes on same zone_1"); + clusterState = addNodes(clusterState, "zone_1", "node1", "node2", "node3", "node4", "node5"); + + logger.info("--> adding five nodes on same zone_2"); + clusterState = addNodes(clusterState, "zone_2", "node6", "node7", "node8", "node9", "node10"); + + logger.info("--> adding five nodes on same zone_3"); + clusterState = addNodes(clusterState, "zone_3", "node11", "node12", "node13", "node14", "node15"); + + final DecommissionNodeAttributeClusterStateTaskExecutor executor = new DecommissionNodeAttributeClusterStateTaskExecutor( + allocationService, + logger + ) { + @Override + protected ClusterState remainingNodesClusterState(ClusterState currentState, DiscoveryNodes.Builder remainingNodesBuilder) { + remainingNodesClusterState.set(super.remainingNodesClusterState(currentState, remainingNodesBuilder)); + return remainingNodesClusterState.get(); + } + }; + + final List tasks = new ArrayList<>(); + // Task 1 with unknown attribute name + tasks.add( + new DecommissionNodeAttributeClusterStateTaskExecutor.Task( + new DecommissionAttribute("unknown_zone_name", Collections.singletonList("unknown_zone_value")), + "unit test zone decommission executor" + ) + ); + // Task 2 with unknown attribute value + tasks.add( + new DecommissionNodeAttributeClusterStateTaskExecutor.Task( + new DecommissionAttribute("zone", Collections.singletonList("unknown_zone_value")), + "unit test zone decommission executor" + ) + ); + + final ClusterStateTaskExecutor.ClusterTasksResult result = executor.execute( + clusterState, + tasks + ); + + ClusterState expectedClusterState = remainingNodesClusterState.get(); + ClusterState actualClusterState = result.resultingState; + + // assert that disassociate dead node tasks is never executed + verify(allocationService, never()).disassociateDeadNodes(eq(expectedClusterState), eq(true), any(String.class)); + + // assert that cluster state remains same + assertEquals(clusterState, actualClusterState); + + // Verify all 15 nodes present in the cluster after decommissioning unknown attribute name + assertEquals(actualClusterState.nodes().getNodes().size(), 15); + } + + private ClusterState addNodes(ClusterState clusterState, String zone, String... nodeIds) { + DiscoveryNodes.Builder nodeBuilder = DiscoveryNodes.builder(clusterState.nodes()); + org.opensearch.common.collect.List.of(nodeIds).forEach(nodeId -> nodeBuilder.add(newNode(nodeId, singletonMap("zone", zone)))); + clusterState = ClusterState.builder(clusterState).nodes(nodeBuilder).build(); + return clusterState; + } + + private DiscoveryNode newNode(String nodeId, Map attributes) { + return new DiscoveryNode(nodeId, buildNewFakeTransportAddress(), attributes, CLUSTER_MANAGER_DATA_ROLES, Version.CURRENT); + } + + final private static Set CLUSTER_MANAGER_DATA_ROLES = Collections.unmodifiableSet( + new HashSet<>(Arrays.asList(DiscoveryNodeRole.CLUSTER_MANAGER_ROLE, DiscoveryNodeRole.DATA_ROLE)) + ); +} From a5509f1c889c32faec173136e5461d3da4739315 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Mon, 8 Aug 2022 20:05:20 +0530 Subject: [PATCH 114/187] Add DecommissionHelper Signed-off-by: Rishab Nahata --- .../decommission/DecommissionHelper.java | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java new file mode 100644 index 0000000000000..6b9e480abcef7 --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java @@ -0,0 +1,58 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.decommission; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.ClusterStateTaskConfig; +import org.opensearch.cluster.ClusterStateTaskListener; +import org.opensearch.cluster.coordination.NodeRemovalClusterStateTaskExecutor; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.node.DiscoveryNodes; +import org.opensearch.cluster.service.ClusterManagerService; +import org.opensearch.common.Priority; +import org.opensearch.common.inject.Inject; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class DecommissionHelper { + + private static final Logger logger = LogManager.getLogger(DecommissionHelper.class); + + private final NodeRemovalClusterStateTaskExecutor nodeRemovalExecutor; + private final ClusterManagerService clusterManagerService; + + DecommissionHelper( + ClusterManagerService clusterManagerService, + NodeRemovalClusterStateTaskExecutor nodeRemovalClusterStateTaskExecutor + ) { + this.nodeRemovalExecutor = nodeRemovalClusterStateTaskExecutor; + this.clusterManagerService = clusterManagerService; + } + + private void handleNodesDecommissionRequest(List nodesToBeDecommissioned, String reason) { + final Map nodesDecommissionTasks = new LinkedHashMap<>(); + nodesToBeDecommissioned.forEach(discoveryNode -> { + final NodeRemovalClusterStateTaskExecutor.Task task = new NodeRemovalClusterStateTaskExecutor.Task( + discoveryNode, reason + ); + nodesDecommissionTasks.put(task, nodeRemovalExecutor); + }); + final String source = "node-decommissioned"; + clusterManagerService.submitStateUpdateTasks( + source, + nodesDecommissionTasks, + ClusterStateTaskConfig.build(Priority.IMMEDIATE), + nodeRemovalExecutor + ); + } +} From 935fcc199609840b5ca0ec1945d2ecd84f7876af Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Wed, 17 Aug 2022 15:46:09 +0530 Subject: [PATCH 115/187] Decommission service implementation with metadata Signed-off-by: Rishab Nahata --- .../org/opensearch/OpenSearchException.java | 7 + .../org/opensearch/cluster/ClusterModule.java | 9 + ...NodeAttributeClusterStateTaskExecutor.java | 141 ---------- .../decommission/DecommissionAttribute.java | 47 ++-- .../DecommissionFailedException.java | 49 ++++ .../decommission/DecommissionHelper.java | 15 +- .../decommission/DecommissionService.java | 223 +++++++++++++++ .../decommission/DecommissionStatus.java | 94 +++++++ .../DecommissionAttributeMetadata.java | 254 ++++++++++++++++++ ...ttributeClusterStateTaskExecutorTests.java | 178 ------------ 10 files changed, 664 insertions(+), 353 deletions(-) delete mode 100644 server/src/main/java/org/opensearch/cluster/coordination/DecommissionNodeAttributeClusterStateTaskExecutor.java create mode 100644 server/src/main/java/org/opensearch/cluster/decommission/DecommissionFailedException.java create mode 100644 server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java create mode 100644 server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java create mode 100644 server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java delete mode 100644 server/src/test/java/org/opensearch/cluster/coordination/DecommissionNodeAttributeClusterStateTaskExecutorTests.java diff --git a/server/src/main/java/org/opensearch/OpenSearchException.java b/server/src/main/java/org/opensearch/OpenSearchException.java index 87efc03734d26..d3e1bef9b6dbb 100644 --- a/server/src/main/java/org/opensearch/OpenSearchException.java +++ b/server/src/main/java/org/opensearch/OpenSearchException.java @@ -34,6 +34,7 @@ import org.opensearch.action.support.replication.ReplicationOperation; import org.opensearch.cluster.action.shard.ShardStateAction; +import org.opensearch.cluster.decommission.DecommissionFailedException; import org.opensearch.common.CheckedFunction; import org.opensearch.common.Nullable; import org.opensearch.common.ParseField; @@ -1608,6 +1609,12 @@ private enum OpenSearchExceptionHandle { org.opensearch.index.shard.PrimaryShardClosedException::new, 162, V_3_0_0 + ), + DECOMMISSION_FAILED_EXCEPTION( + org.opensearch.cluster.decommission.DecommissionFailedException.class, + org.opensearch.cluster.decommission.DecommissionFailedException::new, + 163, + V_2_1_0 ); final Class exceptionClass; diff --git a/server/src/main/java/org/opensearch/cluster/ClusterModule.java b/server/src/main/java/org/opensearch/cluster/ClusterModule.java index f8ba520e465e2..de63369dafc89 100644 --- a/server/src/main/java/org/opensearch/cluster/ClusterModule.java +++ b/server/src/main/java/org/opensearch/cluster/ClusterModule.java @@ -38,6 +38,7 @@ import org.opensearch.cluster.metadata.ComponentTemplateMetadata; import org.opensearch.cluster.metadata.ComposableIndexTemplateMetadata; import org.opensearch.cluster.metadata.DataStreamMetadata; +import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; import org.opensearch.cluster.metadata.IndexGraveyard; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.metadata.Metadata; @@ -191,6 +192,7 @@ public static List getNamedWriteables() { ComposableIndexTemplateMetadata::readDiffFrom ); registerMetadataCustom(entries, DataStreamMetadata.TYPE, DataStreamMetadata::new, DataStreamMetadata::readDiffFrom); + registerMetadataCustom(entries, DecommissionAttributeMetadata.TYPE, DecommissionAttributeMetadata::new, DecommissionAttributeMetadata::readDiffFrom); // Task Status (not Diffable) entries.add(new Entry(Task.Status.class, PersistentTasksNodeService.Status.NAME, PersistentTasksNodeService.Status::new)); return entries; @@ -274,6 +276,13 @@ public static List getNamedXWriteables() { DataStreamMetadata::fromXContent ) ); + entries.add( + new NamedXContentRegistry.Entry( + Metadata.Custom.class, + new ParseField(DecommissionAttributeMetadata.TYPE), + DecommissionAttributeMetadata::fromXContent + ) + ); return entries; } diff --git a/server/src/main/java/org/opensearch/cluster/coordination/DecommissionNodeAttributeClusterStateTaskExecutor.java b/server/src/main/java/org/opensearch/cluster/coordination/DecommissionNodeAttributeClusterStateTaskExecutor.java deleted file mode 100644 index d71cd98d5f25e..0000000000000 --- a/server/src/main/java/org/opensearch/cluster/coordination/DecommissionNodeAttributeClusterStateTaskExecutor.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -package org.opensearch.cluster.coordination; - -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.message.ParameterizedMessage; -import org.opensearch.cluster.ClusterState; -import org.opensearch.cluster.ClusterStateTaskExecutor; -import org.opensearch.cluster.ClusterStateTaskListener; -import org.opensearch.cluster.decommission.DecommissionAttribute; -import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.cluster.node.DiscoveryNodes; -import org.opensearch.cluster.routing.allocation.AllocationService; -import org.opensearch.persistent.PersistentTasksCustomMetadata; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.function.Predicate; - -/** - * Decommissions and shuts down nodes having a given attribute and updates the cluster state - * - * @opensearch.internal - */ -public class DecommissionNodeAttributeClusterStateTaskExecutor - implements - ClusterStateTaskExecutor, - ClusterStateTaskListener { - - private final AllocationService allocationService; - private final Logger logger; - - /** - * Task for the executor. - * - * @opensearch.internal - */ - public static class Task { - - private final DecommissionAttribute decommissionAttribute; - private final String reason; - - public Task(final DecommissionAttribute decommissionAttribute, final String reason) { - this.decommissionAttribute = decommissionAttribute; - this.reason = reason; - } - - public DecommissionAttribute decommissionAttribute() { - return decommissionAttribute; - } - - public String reason() { - return reason; - } - - @Override - public String toString() { - return "Decommission Node Attribute Task{" - + "decommissionAttribute=" - + decommissionAttribute - + ", reason='" - + reason - + '\'' - + '}'; - } - } - - public DecommissionNodeAttributeClusterStateTaskExecutor(final AllocationService allocationService, final Logger logger) { - this.allocationService = allocationService; - this.logger = logger; - } - - @Override - public ClusterTasksResult execute(ClusterState currentState, List tasks) throws Exception { - final DiscoveryNodes.Builder remainingNodesBuilder = DiscoveryNodes.builder(currentState.nodes()); - List nodesToBeRemoved = new ArrayList(); - for (final Task task : tasks) { - final Predicate shouldRemoveNodePredicate = discoveryNode -> nodeHasDecommissionedAttribute(discoveryNode, task); - Iterator nodesIter = currentState.nodes().getNodes().valuesIt(); - while (nodesIter.hasNext()) { - final DiscoveryNode node = nodesIter.next(); - if (shouldRemoveNodePredicate.test(node) && currentState.nodes().nodeExists(node)) { - nodesToBeRemoved.add(node); - } - } - } - if (nodesToBeRemoved.size() <= 0) { - // no nodes to remove, will keep the current cluster state - return ClusterTasksResult.builder() - .successes(tasks) - .build(currentState); - } - for (DiscoveryNode nodeToBeRemoved : nodesToBeRemoved) { - remainingNodesBuilder.remove(nodeToBeRemoved); - } - - final ClusterState remainingNodesClusterState = remainingNodesClusterState(currentState, remainingNodesBuilder); - - return getTaskClusterTasksResult(currentState, tasks, remainingNodesClusterState); - } - - private boolean nodeHasDecommissionedAttribute(DiscoveryNode discoveryNode, Task task) { - String discoveryNodeAttributeValue = discoveryNode.getAttributes().get(task.decommissionAttribute().attributeName()); - return discoveryNodeAttributeValue != null && task.decommissionAttribute().attributeValues().contains(discoveryNodeAttributeValue); - } - - // visible for testing - // hook is used in testing to ensure that correct cluster state is used to test whether a - // rejoin or reroute is needed - protected ClusterState remainingNodesClusterState(final ClusterState currentState, DiscoveryNodes.Builder remainingNodesBuilder) { - return ClusterState.builder(currentState).nodes(remainingNodesBuilder).build(); - } - - protected ClusterTasksResult getTaskClusterTasksResult( - ClusterState currentState, - List tasks, - ClusterState remainingNodesClusterState - ) { - ClusterState ptasksDisassociatedState = PersistentTasksCustomMetadata.disassociateDeadNodes(remainingNodesClusterState); - final ClusterTasksResult.Builder resultBuilder = ClusterTasksResult.< - DecommissionNodeAttributeClusterStateTaskExecutor.Task>builder().successes(tasks); - return resultBuilder.build(allocationService.disassociateDeadNodes(ptasksDisassociatedState, true, describeTasks(tasks))); - } - - @Override - public void onFailure(final String source, final Exception e) { - logger.error(() -> new ParameterizedMessage("unexpected failure during [{}]", source), e); - } - - @Override - public void onNoLongerClusterManager(String source) { - logger.debug("no longer cluster-manager while decommissioning node attribute [{}]", source); - } -} diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java index 6260af2823687..db4e06e854518 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java @@ -18,27 +18,27 @@ public final class DecommissionAttribute implements Writeable { private final String attributeName; - private final List attributeValues; + private final String attributeValue; /** - * Update the attribute values for a given attribute name to decommission + * Update the attribute value for a given attribute name to decommission * * @param decommissionAttribute current decommissioned attribute object - * @param attributeValues values to be updated with + * @param attributeValue attribute value to be updated with */ - public DecommissionAttribute(DecommissionAttribute decommissionAttribute, List attributeValues) { - this(decommissionAttribute.attributeName, attributeValues); + public DecommissionAttribute(DecommissionAttribute decommissionAttribute, String attributeValue) { + this(decommissionAttribute.attributeName, attributeValue); } /** - * Constructs new decommission attribute name values pair + * Constructs new decommission attribute name value pair * * @param attributeName attribute name - * @param attributeValues attribute values + * @param attributeValue attribute value */ - public DecommissionAttribute(String attributeName, List attributeValues) { + public DecommissionAttribute(String attributeName, String attributeValue) { this.attributeName = attributeName; - this.attributeValues = attributeValues; + this.attributeValue = attributeValue; } /** @@ -51,35 +51,35 @@ public String attributeName() { } /** - * Returns attribute values + * Returns attribute value * - * @return attributeValues + * @return attributeValue */ - public List attributeValues() { - return this.attributeValues; + public String attributeValue() { + return this.attributeValue; } public DecommissionAttribute(StreamInput in) throws IOException { attributeName = in.readString(); - attributeValues = in.readStringList(); + attributeValue = in.readString(); } /** - * Writes decommission attribute name values to stream output + * Writes decommission attribute name value to stream output * * @param out stream output */ @Override public void writeTo(StreamOutput out) throws IOException { out.writeString(attributeName); - out.writeStringCollection(attributeValues); + out.writeString(attributeValue); } /** - * Checks if this instance is equal to the other instance in attributeName other than {@link #attributeValues}. + * Checks if this instance is equal to the other instance in attributeName but differ in attribute value {@link #attributeValue}. * - * @param other other decommission attribute name values - * @return {@code true} if both instances equal in attributeName fields but the attributeValues fields + * @param other other decommission attribute name value + * @return {@code true} if both instances equal in attributeName fields but the attributeValue field */ public boolean equalsIgnoreValues(DecommissionAttribute other) { return attributeName.equals(other.attributeName); @@ -93,16 +93,13 @@ public boolean equals(Object o) { DecommissionAttribute that = (DecommissionAttribute) o; if (!attributeName.equals(that.attributeName)) return false; - return attributeValues.equals(that.attributeValues); + return attributeValue.equals(that.attributeValue); } @Override public int hashCode() { - return Objects.hash(attributeName, attributeValues); + return Objects.hash(attributeName, attributeValue); } - @Override - public String toString() { - return "DecommissionAttribute{" + attributeName + "}{" + attributeValues().toString() + "}"; - } + } diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionFailedException.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionFailedException.java new file mode 100644 index 0000000000000..3a611c2488779 --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionFailedException.java @@ -0,0 +1,49 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.decommission; + +import org.opensearch.OpenSearchException; +import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.common.io.stream.StreamOutput; + +import java.io.IOException; + +public class DecommissionFailedException extends OpenSearchException { + + private final DecommissionAttribute decommissionAttribute; + + public DecommissionFailedException(DecommissionAttribute decommissionAttribute, String msg) { + this(decommissionAttribute, msg, null); + } + + public DecommissionFailedException(DecommissionAttribute decommissionAttribute, String msg, Throwable cause) { + super("[" + (decommissionAttribute == null ? "_na" : decommissionAttribute.toString()) + "] " + msg, cause); + this.decommissionAttribute = decommissionAttribute; + } + + public DecommissionFailedException(StreamInput in) throws IOException { + super(in); + decommissionAttribute = new DecommissionAttribute(in); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + decommissionAttribute.writeTo(out); + } + + /** + * Returns decommission attribute + * + * @return decommission attribute + */ + public DecommissionAttribute decommissionAttribute() { + return decommissionAttribute; + } +} diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java index 6b9e480abcef7..d1eb17adc9747 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java @@ -10,15 +10,12 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateTaskConfig; import org.opensearch.cluster.ClusterStateTaskListener; import org.opensearch.cluster.coordination.NodeRemovalClusterStateTaskExecutor; import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.cluster.node.DiscoveryNodes; -import org.opensearch.cluster.service.ClusterManagerService; +import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Priority; -import org.opensearch.common.inject.Inject; import java.util.LinkedHashMap; import java.util.List; @@ -29,17 +26,17 @@ public class DecommissionHelper { private static final Logger logger = LogManager.getLogger(DecommissionHelper.class); private final NodeRemovalClusterStateTaskExecutor nodeRemovalExecutor; - private final ClusterManagerService clusterManagerService; + private final ClusterService clusterService; DecommissionHelper( - ClusterManagerService clusterManagerService, + ClusterService clusterService, NodeRemovalClusterStateTaskExecutor nodeRemovalClusterStateTaskExecutor ) { this.nodeRemovalExecutor = nodeRemovalClusterStateTaskExecutor; - this.clusterManagerService = clusterManagerService; + this.clusterService = clusterService; } - private void handleNodesDecommissionRequest(List nodesToBeDecommissioned, String reason) { + public void handleNodesDecommissionRequest(List nodesToBeDecommissioned, String reason) { final Map nodesDecommissionTasks = new LinkedHashMap<>(); nodesToBeDecommissioned.forEach(discoveryNode -> { final NodeRemovalClusterStateTaskExecutor.Task task = new NodeRemovalClusterStateTaskExecutor.Task( @@ -48,7 +45,7 @@ private void handleNodesDecommissionRequest(List nodesToBeDecommi nodesDecommissionTasks.put(task, nodeRemovalExecutor); }); final String source = "node-decommissioned"; - clusterManagerService.submitStateUpdateTasks( + clusterService.submitStateUpdateTasks( source, nodesDecommissionTasks, ClusterStateTaskConfig.build(Priority.IMMEDIATE), diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java new file mode 100644 index 0000000000000..160b729b14b3a --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -0,0 +1,223 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.decommission; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.message.ParameterizedMessage; +import org.opensearch.action.ActionListener; +import org.opensearch.cluster.ClusterChangedEvent; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.ClusterStateApplier; +import org.opensearch.cluster.ClusterStateUpdateTask; +import org.opensearch.cluster.ack.ClusterStateUpdateResponse; +import org.opensearch.cluster.coordination.NodeRemovalClusterStateTaskExecutor; +import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; +import org.opensearch.cluster.metadata.Metadata; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.routing.allocation.AllocationService; +import org.opensearch.cluster.routing.allocation.decider.AwarenessAllocationDecider; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.Priority; +import org.opensearch.common.inject.Inject; +import org.opensearch.common.settings.ClusterSettings; +import org.opensearch.common.settings.Settings; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportService; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.function.Predicate; + +// do we need to implement ClusterStateApplier -> will a change in cluster state impact this service?? +public class DecommissionService implements ClusterStateApplier { + + private static final Logger logger = LogManager.getLogger(DecommissionService.class); + + private final ClusterService clusterService; + private final TransportService transportService; + private final NodeRemovalClusterStateTaskExecutor nodeRemovalExecutor; + private final ThreadPool threadPool; + private final DecommissionHelper decommissionHelper; + private ClusterState clusterState; + private volatile List awarenessAttributes; + + @Inject + public DecommissionService( + Settings settings, + ClusterSettings clusterSettings, + ClusterService clusterService, + TransportService transportService, + ThreadPool threadPool, + AllocationService allocationService + ) { + this.clusterService = clusterService; + this.transportService = transportService; + this.threadPool = threadPool; + this.clusterState = clusterService.state(); // TODO - check if this is the right way + this.nodeRemovalExecutor = new NodeRemovalClusterStateTaskExecutor(allocationService, logger); + this.decommissionHelper = new DecommissionHelper( + clusterService, + nodeRemovalExecutor + ); + this.awarenessAttributes = AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING.get(settings); + clusterSettings.addSettingsUpdateConsumer( + AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING, + this::setAwarenessAttributes + ); + } + + List getAwarenessAttributes() { + return awarenessAttributes; + } + + private void setAwarenessAttributes(List awarenessAttributes) { + this.awarenessAttributes = awarenessAttributes; + } + + public void initiateAttributeDecommissioning( + final DecommissionAttribute decommissionAttribute, + final ActionListener listener + ) { + /** + * 1. Abdicate master + * 2. Register attribute -> status should be set to INIT + * 3. Trigger weigh away for graceful decommission -> status should be set to DECOMMISSIONING + * 4. Once zone is weighed away -> trigger zone decommission using executor -> status should be set to DECOMMISSIONED on successful response + * 5. Clear voting config + */ + registerDecommissionAttribute(decommissionAttribute, listener); + } + + /** + * Registers new decommissioned attribute metadata in the cluster state + *

+ * This method can be only called on the cluster-manager node. It tries to create a new decommissioned attribute on the master + * and if it was successful it adds new decommissioned attribute to cluster metadata. + *

+ * This method should only be called once the eligible cluster manager node having decommissioned attribute is abdicated + * + * @param decommissionAttribute register decommission attribute in the metadata request + * @param listener register decommission listener + */ + private void registerDecommissionAttribute( + final DecommissionAttribute decommissionAttribute, + final ActionListener listener + ) { + validateAwarenessAttribute(decommissionAttribute, getAwarenessAttributes()); + clusterService.submitStateUpdateTask( + "put_decommission [" + decommissionAttribute + "]", + new ClusterStateUpdateTask(Priority.URGENT) { + @Override + public ClusterState execute(ClusterState currentState) throws Exception { + logger.info("decommission request for attribute [{}] received", decommissionAttribute.toString()); + Metadata metadata = currentState.metadata(); + Metadata.Builder mdBuilder = Metadata.builder(metadata); + DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.custom(DecommissionAttributeMetadata.TYPE); + ensureNoAwarenessAttributeDecommissioned(decommissionAttributeMetadata, decommissionAttribute); + decommissionAttributeMetadata = new DecommissionAttributeMetadata(decommissionAttribute); + mdBuilder.putCustom(DecommissionAttributeMetadata.TYPE, decommissionAttributeMetadata); + return ClusterState.builder(currentState).metadata(mdBuilder).build(); + } + + @Override + public void onFailure(String source, Exception e) { + // TODO - should we put the weights back to zone, since we weighed away the zone before we started registering the metadata + // TODO - should we modify logic of logging for ease of debugging? + if (e instanceof DecommissionFailedException) { + logger.error(() -> new ParameterizedMessage("failed to decommission attribute [{}]", decommissionAttribute.toString()), e); + } else { + clusterService.submitStateUpdateTask( + "decommission_failed", + new ClusterStateUpdateTask(Priority.URGENT) { + @Override + public ClusterState execute(ClusterState currentState) throws Exception { + Metadata metadata = currentState.metadata(); + Metadata.Builder mdBuilder = Metadata.builder(metadata); + logger.info("decommission request for attribute [{}] failed", decommissionAttribute.toString()); + DecommissionAttributeMetadata decommissionAttributeMetadata = new DecommissionAttributeMetadata( + decommissionAttribute, + DecommissionStatus.DECOMMISSION_FAILED + ); + mdBuilder.putCustom(DecommissionAttributeMetadata.TYPE, decommissionAttributeMetadata); + return ClusterState.builder(currentState).metadata(mdBuilder).build(); + } + + @Override + public void onFailure(String source, Exception e) { + logger.error(() -> new ParameterizedMessage( + "failed to mark status as DECOMMISSION_FAILED for decommission attribute [{}]", + decommissionAttribute.toString()), e); +// listener.onFailure(e); + } + } + ); + } + listener.onFailure(e); + } + + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + if (!newState.equals(oldState)) { + // TODO - drain the nodes before decommissioning + failDecommissionedNodes(newState); + listener.onResponse(new ClusterStateUpdateResponse(true)); + } + listener.onResponse(new ClusterStateUpdateResponse(false)); + } + } + ); + } + + private static void validateAwarenessAttribute(final DecommissionAttribute decommissionAttribute, List awarenessAttributes) { + if (!awarenessAttributes.contains(decommissionAttribute.attributeName())) { + throw new DecommissionFailedException(decommissionAttribute, "invalid awareness attribute requested for decommissioning"); + } + // TODO - should attribute value be part of force zone values? If yes, read setting and throw exception if not found + } + + private static void ensureNoAwarenessAttributeDecommissioned( + DecommissionAttributeMetadata decommissionAttributeMetadata, + DecommissionAttribute decommissionAttribute + ) { + // If the previous decommission request failed, we will allow the request to pass this check + if (decommissionAttributeMetadata != null && !decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_FAILED)) { + throw new DecommissionFailedException(decommissionAttribute, "one awareness attribute already decommissioned, " + + "recommission before triggering another decommission"); + } + } + + private void failDecommissionedNodes(ClusterState state) { + DecommissionAttributeMetadata decommissionAttributeMetadata = state.metadata().custom(DecommissionAttributeMetadata.TYPE); + assert decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSIONING) : "unexpected status encountered while decommissioning nodes"; + DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute(); + List nodesToBeDecommissioned = new ArrayList<>(); + final Predicate shouldRemoveNodePredicate = discoveryNode -> nodeHasDecommissionedAttribute(discoveryNode, decommissionAttribute); + Iterator nodesIter = state.nodes().getNodes().valuesIt(); + while (nodesIter.hasNext()) { + final DiscoveryNode node = nodesIter.next(); + if (shouldRemoveNodePredicate.test(node)) { + nodesToBeDecommissioned.add(node); + } + } + decommissionHelper.handleNodesDecommissionRequest(nodesToBeDecommissioned, "nodes-decommissioned"); + } + + private static boolean nodeHasDecommissionedAttribute(DiscoveryNode discoveryNode, DecommissionAttribute decommissionAttribute) { + return discoveryNode.getAttributes().get( + decommissionAttribute.attributeName() + ).equals(decommissionAttribute.attributeValue()); + } + + @Override + public void applyClusterState(ClusterChangedEvent event) { + clusterState = event.state(); + } +} diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java new file mode 100644 index 0000000000000..8f4ca3a6f578a --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java @@ -0,0 +1,94 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.decommission; + +public enum DecommissionStatus { + /** + * Decommission process is initiated + */ + INIT((byte) 0), + /** + * Decommission process has started, decommissioned nodes should be weighed away + */ + DECOMMISSIONING((byte) 1), + /** + * Decommissioning awareness attribute completed + */ + DECOMMISSIONED((byte) 2), + /** + * Decommission request failed + */ + DECOMMISSION_FAILED((byte) 3), + /** + * Recommission request received, recommissioning process has started + */ + RECOMMISSIONING((byte) 4), + /** + * Recommission request failed. No nodes should fail to join the cluster with decommission exception + */ + RECOMMISSION_FAILED((byte) 5); + + private final byte value; + + DecommissionStatus(byte value) { + this.value = value; + } + + /** + * Returns code that represents the decommission state + * + * @return code for the state + */ + public byte value() { + return value; + } + + /** + * Generate decommission state from code + * + * @param value the state code + * @return state + */ + public static DecommissionStatus fromValue(byte value) { + switch (value) { + case 0: + return INIT; + case 1: + return DECOMMISSIONING; + case 2: + return DECOMMISSIONED; + case 3: + return DECOMMISSION_FAILED; + case 4: + return RECOMMISSIONING; + case 5: + return RECOMMISSION_FAILED; + default: + throw new IllegalArgumentException("No decommission state for value [" + value + "]"); + } + } + + public static DecommissionStatus fromString(String status) { + if ("init".equals(status)) { + return INIT; + } else if ("decommissioning".equals(status)) { + return DECOMMISSIONING; + } else if ("decommissioned".equals(status)) { + return DECOMMISSIONED; + } else if ("decommission_failed".equals(status)) { + return DECOMMISSION_FAILED; + } else if ("recommissioning".equals(status)) { + return RECOMMISSIONING; + } else if ("recommission_failed".equals(status)) { + return RECOMMISSION_FAILED; + } + throw new IllegalStateException("No status match for [" + status + "]"); + } +} + diff --git a/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java b/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java new file mode 100644 index 0000000000000..dd0c78d7519b1 --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java @@ -0,0 +1,254 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.metadata; + +import org.opensearch.OpenSearchParseException; +import org.opensearch.Version; +import org.opensearch.cluster.AbstractNamedDiffable; +import org.opensearch.cluster.NamedDiff; +import org.opensearch.cluster.decommission.DecommissionAttribute; +import org.opensearch.cluster.decommission.DecommissionStatus; +import org.opensearch.cluster.metadata.Metadata.Custom; +import org.opensearch.common.Nullable; +import org.opensearch.common.Strings; +import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.common.xcontent.ToXContent; +import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.xcontent.XContentParser; + +import java.io.IOException; +import java.util.EnumSet; +import java.util.Objects; + +/** + * Contains metadata about decommission attribute + * + * @opensearch.internal + */ +public class DecommissionAttributeMetadata extends AbstractNamedDiffable implements Custom { + + public static final String TYPE = "decommissionedAttribute"; + + private final DecommissionAttribute decommissionAttribute; + private final DecommissionStatus status; + public static final String attributeType = "awareness"; + + /** + * Constructs new decommission attribute metadata with given status + * + * @param decommissionAttribute attribute details + * @param status current status of the attribute decommission + */ + public DecommissionAttributeMetadata(DecommissionAttribute decommissionAttribute, DecommissionStatus status) { + this.decommissionAttribute = decommissionAttribute; + this.status = status; + } + + /** + * Constructs new decommission attribute metadata with status as {@link DecommissionStatus#INIT} + * + * @param decommissionAttribute attribute details + */ + public DecommissionAttributeMetadata(DecommissionAttribute decommissionAttribute) { + this.decommissionAttribute = decommissionAttribute; + this.status = DecommissionStatus.INIT; + } + + /** + * Returns the current decommissioned attribute + * + * @return decommissioned attributes + */ + public DecommissionAttribute decommissionAttribute() { + return this.decommissionAttribute; + } + + /** + * Returns the current status of the attribute decommission + * + * @return attribute type + */ + public DecommissionStatus status() { + return this.status; + } + + public DecommissionAttributeMetadata withUpdatedStatus( + DecommissionAttributeMetadata metadata, + DecommissionStatus status) { + return new DecommissionAttributeMetadata( + metadata.decommissionAttribute(), + status + ); + } + + /** + * Creates a new instance with a updated attribute value. + * + * @param metadata current metadata + * @param attributeValue new attribute value + * @return new instance with updated attribute value and status as DecommissionStatus.INIT + */ + public DecommissionAttributeMetadata withUpdatedAttributeValue( + DecommissionAttributeMetadata metadata, + String attributeValue + ) { + return new DecommissionAttributeMetadata( + new DecommissionAttribute(metadata.decommissionAttribute, attributeValue), + DecommissionStatus.INIT + ); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + DecommissionAttributeMetadata that = (DecommissionAttributeMetadata) o; + + if (!status.equals(that.status)) return false; + return decommissionAttribute.equals(that.decommissionAttribute); + } + + /** + * Checks if this instance and the given instance share the same decommissioned attributeName + * and only differ in the attributeValue {@link DecommissionAttribute#attributeValue()} + * + * @param other other decommission attribute metadata + * @return {@code true} iff both instances contain the same attributeName + */ + public boolean equalsIgnoreValue(@Nullable DecommissionAttributeMetadata other) { + if (other == null) { + return false; + } + if (!status.equals(other.status)) return false; + return decommissionAttribute.equalsIgnoreValues(other.decommissionAttribute); + } + + @Override + public int hashCode() { + return Objects.hash(attributeType, decommissionAttribute, status); + } + + /** + * {@inheritDoc} + */ + @Override + public String getWriteableName() { + return TYPE; + } + + @Override + public Version getMinimalSupportedVersion() { + return Version.CURRENT.minimumCompatibilityVersion(); + } + + public DecommissionAttributeMetadata(StreamInput in) throws IOException { + this.status = DecommissionStatus.fromValue(in.readByte()); + this.decommissionAttribute = new DecommissionAttribute(in); + } + + public static NamedDiff readDiffFrom(StreamInput in) throws IOException { + return readDiffFrom(Custom.class, TYPE, in); + } + + /** + * {@inheritDoc} + */ + @Override + public void writeTo(StreamOutput out) throws IOException { + decommissionAttribute.writeTo(out); + out.writeByte(status.value()); + out.writeString(attributeType); + } + + public static DecommissionAttributeMetadata fromXContent(XContentParser parser) throws IOException { + XContentParser.Token token; + DecommissionAttribute decommissionAttribute = null; + DecommissionStatus status = null; + if ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + String currentFieldName = parser.currentName(); + if (attributeType.equals(currentFieldName)) { + if (parser.nextToken() != XContentParser.Token.START_OBJECT) { + throw new OpenSearchParseException("failed to parse decommission attribute type [{}], expected object", attributeType); + } + token = parser.nextToken(); + if (token != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + String fieldName = parser.currentName(); + String value; + token = parser.nextToken(); + if (token != XContentParser.Token.VALUE_STRING) { + value = parser.text(); + } else { + throw new OpenSearchParseException("failed to parse attribute [{}], expected string for attribute value", fieldName); + } + decommissionAttribute = new DecommissionAttribute(fieldName, value); + } else { + throw new OpenSearchParseException("failed to parse attribute type [{}], unexpected type", attributeType); + } + } else { + throw new OpenSearchParseException("failed to parse attribute type [{}]", attributeType); + } + } else if ("status".equals(currentFieldName)) { + if (parser.nextToken() != XContentParser.Token.VALUE_STRING) { + throw new OpenSearchParseException("failed to parse status of decommissioning, expected string but found unknown type"); + } + status = DecommissionStatus.fromString(parser.text()); + } else { + throw new OpenSearchParseException( + "unknown field found [{}], failed to parse the decommission attribute", + currentFieldName + ); + } + } + } + return new DecommissionAttributeMetadata(decommissionAttribute, status); + } + + /** + * {@inheritDoc} + */ + @Override + public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params) throws IOException { + toXContent(decommissionAttribute, status, attributeType, builder, params); + return builder; + } + + @Override + public EnumSet context() { + return Metadata.API_AND_GATEWAY; + } + + /** + * @param decommissionAttribute decommission attribute + * @param status decommission status + * @param attributeType attribute type + * @param builder XContent builder + * @param params serialization parameters + */ + public static void toXContent( + DecommissionAttribute decommissionAttribute, + DecommissionStatus status, + String attributeType, + XContentBuilder builder, + ToXContent.Params params + ) throws IOException { + builder.startObject(attributeType); + builder.field(decommissionAttribute.attributeName(), decommissionAttribute.attributeValue()); + builder.endObject(); + builder.field("status", status); + } + + @Override + public String toString() { + return Strings.toString(this); + } +} diff --git a/server/src/test/java/org/opensearch/cluster/coordination/DecommissionNodeAttributeClusterStateTaskExecutorTests.java b/server/src/test/java/org/opensearch/cluster/coordination/DecommissionNodeAttributeClusterStateTaskExecutorTests.java deleted file mode 100644 index 204d31f18e2cf..0000000000000 --- a/server/src/test/java/org/opensearch/cluster/coordination/DecommissionNodeAttributeClusterStateTaskExecutorTests.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -package org.opensearch.cluster.coordination; - -import org.opensearch.Version; -import org.opensearch.cluster.ClusterName; -import org.opensearch.cluster.ClusterState; -import org.opensearch.cluster.ClusterStateTaskExecutor; -import org.opensearch.cluster.decommission.DecommissionAttribute; -import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.cluster.node.DiscoveryNodeRole; -import org.opensearch.cluster.node.DiscoveryNodes; -import org.opensearch.cluster.routing.allocation.AllocationService; -import org.opensearch.test.OpenSearchTestCase; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.atomic.AtomicReference; - -import static java.util.Collections.singletonMap; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -public class DecommissionNodeAttributeClusterStateTaskExecutorTests extends OpenSearchTestCase { - - public void testRemoveNodesForDecommissionedAttribute() throws Exception { - final AllocationService allocationService = mock(AllocationService.class); - when(allocationService.disassociateDeadNodes(any(ClusterState.class), eq(true), any(String.class))).thenAnswer( - im -> im.getArguments()[0] - ); - final AtomicReference remainingNodesClusterState = new AtomicReference<>(); - ClusterState clusterState = ClusterState.builder(new ClusterName("test")).build(); - - logger.info("--> adding five nodes on same zone_1"); - clusterState = addNodes(clusterState, "zone_1", "node1", "node2", "node3", "node4", "node5"); - - logger.info("--> adding five nodes on same zone_2"); - clusterState = addNodes(clusterState, "zone_2", "node6", "node7", "node8", "node9", "node10"); - - logger.info("--> adding five nodes on same zone_3"); - clusterState = addNodes(clusterState, "zone_3", "node11", "node12", "node13", "node14", "node15"); - - final DecommissionNodeAttributeClusterStateTaskExecutor executor = new DecommissionNodeAttributeClusterStateTaskExecutor( - allocationService, - logger - ) { - @Override - protected ClusterState remainingNodesClusterState(ClusterState currentState, DiscoveryNodes.Builder remainingNodesBuilder) { - remainingNodesClusterState.set(super.remainingNodesClusterState(currentState, remainingNodesBuilder)); - return remainingNodesClusterState.get(); - } - }; - - final List tasks = new ArrayList<>(); - tasks.add( - new DecommissionNodeAttributeClusterStateTaskExecutor.Task( - new DecommissionAttribute("zone", Collections.singletonList("zone_3")), - "unit test zone decommission executor" - ) - ); - - final ClusterStateTaskExecutor.ClusterTasksResult result = executor.execute( - clusterState, - tasks - ); - - ClusterState expectedClusterState = remainingNodesClusterState.get(); - ClusterState actualClusterState = result.resultingState; - - // Assert cluster state is updated and is successful - verify(allocationService).disassociateDeadNodes(eq(expectedClusterState), eq(true), any(String.class)); - assertEquals(actualClusterState, expectedClusterState); - assertTrue(result.executionResults.get(tasks.get(0)).isSuccess()); - - // Verify only 10 nodes present in the cluster after decommissioning - assertEquals(actualClusterState.nodes().getNodes().size(), 10); - - // Verify no nodes has attribute (zone, zone_3) - Iterator currDiscoveryNodeIterator = actualClusterState.nodes().getNodes().valuesIt(); - while (currDiscoveryNodeIterator.hasNext()) { - final DiscoveryNode node = currDiscoveryNodeIterator.next(); - assertNotEquals(node.getAttributes().get("zone"), "zone_3"); - } - } - - public void testSameClusterStateAfterExecutionForUnknownAttributeNameAndValue() throws Exception { - final AllocationService allocationService = mock(AllocationService.class); - when(allocationService.disassociateDeadNodes(any(ClusterState.class), eq(true), any(String.class))).thenAnswer( - im -> im.getArguments()[0] - ); - final AtomicReference remainingNodesClusterState = new AtomicReference<>(); - ClusterState clusterState = ClusterState.builder(new ClusterName("test")).build(); - - logger.info("--> adding five nodes on same zone_1"); - clusterState = addNodes(clusterState, "zone_1", "node1", "node2", "node3", "node4", "node5"); - - logger.info("--> adding five nodes on same zone_2"); - clusterState = addNodes(clusterState, "zone_2", "node6", "node7", "node8", "node9", "node10"); - - logger.info("--> adding five nodes on same zone_3"); - clusterState = addNodes(clusterState, "zone_3", "node11", "node12", "node13", "node14", "node15"); - - final DecommissionNodeAttributeClusterStateTaskExecutor executor = new DecommissionNodeAttributeClusterStateTaskExecutor( - allocationService, - logger - ) { - @Override - protected ClusterState remainingNodesClusterState(ClusterState currentState, DiscoveryNodes.Builder remainingNodesBuilder) { - remainingNodesClusterState.set(super.remainingNodesClusterState(currentState, remainingNodesBuilder)); - return remainingNodesClusterState.get(); - } - }; - - final List tasks = new ArrayList<>(); - // Task 1 with unknown attribute name - tasks.add( - new DecommissionNodeAttributeClusterStateTaskExecutor.Task( - new DecommissionAttribute("unknown_zone_name", Collections.singletonList("unknown_zone_value")), - "unit test zone decommission executor" - ) - ); - // Task 2 with unknown attribute value - tasks.add( - new DecommissionNodeAttributeClusterStateTaskExecutor.Task( - new DecommissionAttribute("zone", Collections.singletonList("unknown_zone_value")), - "unit test zone decommission executor" - ) - ); - - final ClusterStateTaskExecutor.ClusterTasksResult result = executor.execute( - clusterState, - tasks - ); - - ClusterState expectedClusterState = remainingNodesClusterState.get(); - ClusterState actualClusterState = result.resultingState; - - // assert that disassociate dead node tasks is never executed - verify(allocationService, never()).disassociateDeadNodes(eq(expectedClusterState), eq(true), any(String.class)); - - // assert that cluster state remains same - assertEquals(clusterState, actualClusterState); - - // Verify all 15 nodes present in the cluster after decommissioning unknown attribute name - assertEquals(actualClusterState.nodes().getNodes().size(), 15); - } - - private ClusterState addNodes(ClusterState clusterState, String zone, String... nodeIds) { - DiscoveryNodes.Builder nodeBuilder = DiscoveryNodes.builder(clusterState.nodes()); - org.opensearch.common.collect.List.of(nodeIds).forEach(nodeId -> nodeBuilder.add(newNode(nodeId, singletonMap("zone", zone)))); - clusterState = ClusterState.builder(clusterState).nodes(nodeBuilder).build(); - return clusterState; - } - - private DiscoveryNode newNode(String nodeId, Map attributes) { - return new DiscoveryNode(nodeId, buildNewFakeTransportAddress(), attributes, CLUSTER_MANAGER_DATA_ROLES, Version.CURRENT); - } - - final private static Set CLUSTER_MANAGER_DATA_ROLES = Collections.unmodifiableSet( - new HashSet<>(Arrays.asList(DiscoveryNodeRole.CLUSTER_MANAGER_ROLE, DiscoveryNodeRole.DATA_ROLE)) - ); -} From 1ec04c660ba843e26ad8ab7cbde3df209a81f709 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Wed, 17 Aug 2022 17:38:25 +0530 Subject: [PATCH 116/187] Fixes Signed-off-by: Rishab Nahata --- .../cluster/decommission/DecommissionAttribute.java | 8 +++++++- .../cluster/decommission/DecommissionService.java | 6 ++++-- .../cluster/metadata/DecommissionAttributeMetadata.java | 5 ++--- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java index db4e06e854518..15c17ae4b7ae1 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java @@ -13,7 +13,6 @@ import org.opensearch.common.io.stream.Writeable; import java.io.IOException; -import java.util.List; import java.util.Objects; public final class DecommissionAttribute implements Writeable { @@ -102,4 +101,11 @@ public int hashCode() { } + @Override + public String toString() { + return "DecommissionAttribute{" + + "attributeName='" + attributeName + '\'' + + ", attributeValue='" + attributeValue + '\'' + + '}'; + } } diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 160b729b14b3a..d932a45401530 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -169,8 +169,9 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS // TODO - drain the nodes before decommissioning failDecommissionedNodes(newState); listener.onResponse(new ClusterStateUpdateResponse(true)); + } else { + listener.onResponse(new ClusterStateUpdateResponse(false)); } - listener.onResponse(new ClusterStateUpdateResponse(false)); } } ); @@ -196,7 +197,8 @@ private static void ensureNoAwarenessAttributeDecommissioned( private void failDecommissionedNodes(ClusterState state) { DecommissionAttributeMetadata decommissionAttributeMetadata = state.metadata().custom(DecommissionAttributeMetadata.TYPE); - assert decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSIONING) : "unexpected status encountered while decommissioning nodes"; + // TODO update the status check to DECOMMISSIONING once graceful decommission is implemented + assert decommissionAttributeMetadata.status().equals(DecommissionStatus.INIT) : "unexpected status encountered while decommissioning nodes"; DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute(); List nodesToBeDecommissioned = new ArrayList<>(); final Predicate shouldRemoveNodePredicate = discoveryNode -> nodeHasDecommissionedAttribute(discoveryNode, decommissionAttribute); diff --git a/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java b/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java index dd0c78d7519b1..869576f0ea070 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java @@ -150,8 +150,8 @@ public Version getMinimalSupportedVersion() { } public DecommissionAttributeMetadata(StreamInput in) throws IOException { - this.status = DecommissionStatus.fromValue(in.readByte()); this.decommissionAttribute = new DecommissionAttribute(in); + this.status = DecommissionStatus.fromValue(in.readByte()); } public static NamedDiff readDiffFrom(StreamInput in) throws IOException { @@ -165,7 +165,6 @@ public static NamedDiff readDiffFrom(StreamInput in) throws IOException public void writeTo(StreamOutput out) throws IOException { decommissionAttribute.writeTo(out); out.writeByte(status.value()); - out.writeString(attributeType); } public static DecommissionAttributeMetadata fromXContent(XContentParser parser) throws IOException { @@ -185,7 +184,7 @@ public static DecommissionAttributeMetadata fromXContent(XContentParser parser) String fieldName = parser.currentName(); String value; token = parser.nextToken(); - if (token != XContentParser.Token.VALUE_STRING) { + if (token == XContentParser.Token.VALUE_STRING) { value = parser.text(); } else { throw new OpenSearchParseException("failed to parse attribute [{}], expected string for attribute value", fieldName); From 4a836ef33a4ff2202414b4cbeac7f580006ed42e Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Wed, 17 Aug 2022 19:11:09 +0530 Subject: [PATCH 117/187] Master abdication Signed-off-by: Rishab Nahata --- .../decommission/DecommissionService.java | 60 +++++++++++++++++-- 1 file changed, 56 insertions(+), 4 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index d932a45401530..40d848f81483c 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -12,6 +12,9 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; import org.opensearch.action.ActionListener; +import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsAction; +import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsRequest; +import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsResponse; import org.opensearch.cluster.ClusterChangedEvent; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateApplier; @@ -26,11 +29,15 @@ import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Priority; import org.opensearch.common.inject.Inject; +import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Settings; import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportException; +import org.opensearch.transport.TransportResponseHandler; import org.opensearch.transport.TransportService; +import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -61,7 +68,6 @@ public DecommissionService( this.clusterService = clusterService; this.transportService = transportService; this.threadPool = threadPool; - this.clusterState = clusterService.state(); // TODO - check if this is the right way this.nodeRemovalExecutor = new NodeRemovalClusterStateTaskExecutor(allocationService, logger); this.decommissionHelper = new DecommissionHelper( clusterService, @@ -84,16 +90,62 @@ private void setAwarenessAttributes(List awarenessAttributes) { public void initiateAttributeDecommissioning( final DecommissionAttribute decommissionAttribute, - final ActionListener listener + final ActionListener listener, + ClusterState state ) { - /** + /* * 1. Abdicate master * 2. Register attribute -> status should be set to INIT * 3. Trigger weigh away for graceful decommission -> status should be set to DECOMMISSIONING * 4. Once zone is weighed away -> trigger zone decommission using executor -> status should be set to DECOMMISSIONED on successful response * 5. Clear voting config */ - registerDecommissionAttribute(decommissionAttribute, listener); + this.clusterState = state; + abdicateDecommissionedClusterManagerNodes(decommissionAttribute, listener); + } + + private void abdicateDecommissionedClusterManagerNodes( + DecommissionAttribute decommissionAttribute, + final ActionListener listener + ) { + final Predicate shouldAbdicatePredicate = discoveryNode -> nodeHasDecommissionedAttribute(discoveryNode, decommissionAttribute); + List clusterManagerNodesToBeDecommissioned = new ArrayList<>(); + Iterator clusterManagerNodesIter = clusterState.nodes().getClusterManagerNodes().valuesIt(); + while (clusterManagerNodesIter.hasNext()) { + final DiscoveryNode node = clusterManagerNodesIter.next(); + if (shouldAbdicatePredicate.test(node)) { + clusterManagerNodesToBeDecommissioned.add(node.getName()); + } + } + transportService.sendRequest( + transportService.getLocalNode(), + AddVotingConfigExclusionsAction.NAME, + new AddVotingConfigExclusionsRequest(clusterManagerNodesToBeDecommissioned.toArray(String[]::new)), + new TransportResponseHandler() { + @Override + public void handleResponse(AddVotingConfigExclusionsResponse response) { + logger.info("successfully removed decommissioned cluster manager eligible nodes from voting config [{}], " + + "proceeding to drain the decommissioned nodes", response.toString()); + registerDecommissionAttribute(decommissionAttribute, listener); + } + + @Override + public void handleException(TransportException exp) { + logger.debug(new ParameterizedMessage( + "failure in removing decommissioned cluster manager eligible nodes from voting config"), exp); + } + + @Override + public String executor() { + return ThreadPool.Names.SAME; + } + + @Override + public AddVotingConfigExclusionsResponse read(StreamInput in) throws IOException { + return new AddVotingConfigExclusionsResponse(in); + } + } + ); } /** From 1b7bcbbc1de6cd9aa1e845492f728b039280476c Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Wed, 17 Aug 2022 19:56:25 +0530 Subject: [PATCH 118/187] Fixes Signed-off-by: Rishab Nahata --- .../cluster/decommission/DecommissionService.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 40d848f81483c..48db0e06edb6a 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -101,6 +101,7 @@ public void initiateAttributeDecommissioning( * 5. Clear voting config */ this.clusterState = state; + logger.info("initiating awareness attribute [{}] decommissioning", decommissionAttribute.toString()); abdicateDecommissionedClusterManagerNodes(decommissionAttribute, listener); } @@ -124,8 +125,8 @@ private void abdicateDecommissionedClusterManagerNodes( new TransportResponseHandler() { @Override public void handleResponse(AddVotingConfigExclusionsResponse response) { - logger.info("successfully removed decommissioned cluster manager eligible nodes from voting config [{}], " + - "proceeding to drain the decommissioned nodes", response.toString()); + logger.info("successfully removed decommissioned cluster manager eligible nodes [{}] from voting config, " + + "proceeding to drain the decommissioned nodes", clusterManagerNodesToBeDecommissioned.toString()); registerDecommissionAttribute(decommissionAttribute, listener); } @@ -186,6 +187,7 @@ public void onFailure(String source, Exception e) { if (e instanceof DecommissionFailedException) { logger.error(() -> new ParameterizedMessage("failed to decommission attribute [{}]", decommissionAttribute.toString()), e); } else { + // could be due to on longer cluster manager clusterService.submitStateUpdateTask( "decommission_failed", new ClusterStateUpdateTask(Priority.URGENT) { From 58a5995097459d0be17987b868eef3e38fbbd165 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Wed, 17 Aug 2022 20:28:28 +0530 Subject: [PATCH 119/187] Update join validator to validate decommissioned node join request Signed-off-by: Rishab Nahata --- .../org/opensearch/OpenSearchException.java | 6 ++++ .../coordination/JoinTaskExecutor.java | 36 ++++++++++++++++--- .../NodeDecommissionedException.java | 32 +++++++++++++++++ 3 files changed, 70 insertions(+), 4 deletions(-) create mode 100644 server/src/main/java/org/opensearch/cluster/decommission/NodeDecommissionedException.java diff --git a/server/src/main/java/org/opensearch/OpenSearchException.java b/server/src/main/java/org/opensearch/OpenSearchException.java index d3e1bef9b6dbb..44d17cbca5652 100644 --- a/server/src/main/java/org/opensearch/OpenSearchException.java +++ b/server/src/main/java/org/opensearch/OpenSearchException.java @@ -1615,6 +1615,12 @@ private enum OpenSearchExceptionHandle { org.opensearch.cluster.decommission.DecommissionFailedException::new, 163, V_2_1_0 + ), + NODE_DECOMMISSIONED_EXCEPTION( + org.opensearch.cluster.decommission.NodeDecommissionedException.class, + org.opensearch.cluster.decommission.NodeDecommissionedException::new, + 163, + V_2_1_0 ); final Class exceptionClass; diff --git a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java index 5afdb5b12db23..dca706c250a20 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java @@ -39,6 +39,9 @@ import org.opensearch.cluster.ClusterStateTaskExecutor; import org.opensearch.cluster.NotClusterManagerException; import org.opensearch.cluster.block.ClusterBlocks; +import org.opensearch.cluster.decommission.DecommissionAttribute; +import org.opensearch.cluster.decommission.NodeDecommissionedException; +import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; @@ -107,7 +110,9 @@ public boolean isBecomeClusterManagerTask() { return reason.equals(BECOME_MASTER_TASK_REASON) || reason.equals(BECOME_CLUSTER_MANAGER_TASK_REASON); } - /** @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #isBecomeClusterManagerTask()} */ + /** + * @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #isBecomeClusterManagerTask()} + */ @Deprecated public boolean isBecomeMasterTask() { return isBecomeClusterManagerTask(); @@ -358,6 +363,7 @@ public boolean runOnlyOnClusterManager() { /** * a task indicates that the current node should become master + * * @deprecated As of 2.0, because supporting inclusive language, replaced by {@link #newBecomeClusterManagerTask()} */ @Deprecated @@ -384,8 +390,9 @@ public static Task newFinishElectionTask() { * Ensures that all indices are compatible with the given node version. This will ensure that all indices in the given metadata * will not be created with a newer version of opensearch as well as that all indices are newer or equal to the minimum index * compatibility version. - * @see Version#minimumIndexCompatibilityVersion() + * * @throws IllegalStateException if any index is incompatible with the given version + * @see Version#minimumIndexCompatibilityVersion() */ public static void ensureIndexCompatibility(final Version nodeVersion, Metadata metadata) { Version supportedIndexVersion = nodeVersion.minimumIndexCompatibilityVersion(); @@ -415,14 +422,18 @@ public static void ensureIndexCompatibility(final Version nodeVersion, Metadata } } - /** ensures that the joining node has a version that's compatible with all current nodes*/ + /** + * ensures that the joining node has a version that's compatible with all current nodes + */ public static void ensureNodesCompatibility(final Version joiningNodeVersion, DiscoveryNodes currentNodes) { final Version minNodeVersion = currentNodes.getMinNodeVersion(); final Version maxNodeVersion = currentNodes.getMaxNodeVersion(); ensureNodesCompatibility(joiningNodeVersion, minNodeVersion, maxNodeVersion); } - /** ensures that the joining node has a version that's compatible with a given version range */ + /** + * ensures that the joining node has a version that's compatible with a given version range + */ public static void ensureNodesCompatibility(Version joiningNodeVersion, Version minClusterNodeVersion, Version maxClusterNodeVersion) { assert minClusterNodeVersion.onOrBefore(maxClusterNodeVersion) : minClusterNodeVersion + " > " + maxClusterNodeVersion; if (joiningNodeVersion.isCompatible(maxClusterNodeVersion) == false) { @@ -466,6 +477,22 @@ public static void ensureMajorVersionBarrier(Version joiningNodeVersion, Version } } + public static void ensureNodeNotDecommissioned(DiscoveryNode node, Metadata metadata) { + DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.custom(DecommissionAttributeMetadata.TYPE); + if (decommissionAttributeMetadata != null) { + DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute(); + if (decommissionAttribute != null) { + if (node.getAttributes().get(decommissionAttribute.attributeName()).equals(decommissionAttribute.attributeValue())) { + throw new NodeDecommissionedException( + "node has decommissioned attribute [" + + decommissionAttribute.toString() + + "]." + ); + } + } + } + } + public static Collection> addBuiltInJoinValidators( Collection> onJoinValidators ) { @@ -473,6 +500,7 @@ public static Collection> addBuiltInJoin validators.add((node, state) -> { ensureNodesCompatibility(node.getVersion(), state.getNodes()); ensureIndexCompatibility(node.getVersion(), state.getMetadata()); + ensureNodeNotDecommissioned(node, state.getMetadata()); }); validators.addAll(onJoinValidators); return Collections.unmodifiableCollection(validators); diff --git a/server/src/main/java/org/opensearch/cluster/decommission/NodeDecommissionedException.java b/server/src/main/java/org/opensearch/cluster/decommission/NodeDecommissionedException.java new file mode 100644 index 0000000000000..d4ca4679a0872 --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/decommission/NodeDecommissionedException.java @@ -0,0 +1,32 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.decommission; + +import org.opensearch.OpenSearchException; +import org.opensearch.common.io.stream.StreamInput; + +import java.io.IOException; + +/** + * This exception is thrown if the node is decommissioned by @{@link DecommissionService} + * and this nodes needs to be removed from the cluster + * + * @opensearch.internal + */ + +public class NodeDecommissionedException extends OpenSearchException { + + public NodeDecommissionedException(String msg, Object... args) { + super(msg, args); + } + + public NodeDecommissionedException(StreamInput in) throws IOException { + super(in); + } +} From c3c4712812a6770e73e8bd5d7853ef05f8d79618 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Thu, 18 Aug 2022 10:57:37 +0530 Subject: [PATCH 120/187] Clear voting config after decommissioning Signed-off-by: Rishab Nahata --- .../decommission/DecommissionService.java | 48 +++++++++++++++++-- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 48db0e06edb6a..3971931180656 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -15,10 +15,14 @@ import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsAction; import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsRequest; import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsResponse; +import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsAction; +import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsRequest; +import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsResponse; import org.opensearch.cluster.ClusterChangedEvent; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateApplier; import org.opensearch.cluster.ClusterStateUpdateTask; +import org.opensearch.cluster.NotClusterManagerException; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; import org.opensearch.cluster.coordination.NodeRemovalClusterStateTaskExecutor; import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; @@ -34,6 +38,7 @@ import org.opensearch.common.settings.Settings; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportException; +import org.opensearch.transport.TransportResponse; import org.opensearch.transport.TransportResponseHandler; import org.opensearch.transport.TransportService; @@ -43,7 +48,6 @@ import java.util.List; import java.util.function.Predicate; -// do we need to implement ClusterStateApplier -> will a change in cluster state impact this service?? public class DecommissionService implements ClusterStateApplier { private static final Logger logger = LogManager.getLogger(DecommissionService.class); @@ -102,10 +106,10 @@ public void initiateAttributeDecommissioning( */ this.clusterState = state; logger.info("initiating awareness attribute [{}] decommissioning", decommissionAttribute.toString()); - abdicateDecommissionedClusterManagerNodes(decommissionAttribute, listener); + excludeDecommissionedClusterManagerNodesFromVotingConfig(decommissionAttribute, listener); } - private void abdicateDecommissionedClusterManagerNodes( + private void excludeDecommissionedClusterManagerNodesFromVotingConfig( DecommissionAttribute decommissionAttribute, final ActionListener listener ) { @@ -149,6 +153,38 @@ public AddVotingConfigExclusionsResponse read(StreamInput in) throws IOException ); } + private void clearVotingConfigAfterSuccessfulDecommission() { + final ClearVotingConfigExclusionsRequest clearVotingConfigExclusionsRequest = new ClearVotingConfigExclusionsRequest(); + clearVotingConfigExclusionsRequest.setWaitForRemoval(true); + transportService.sendRequest( + transportService.getLocalNode(), + ClearVotingConfigExclusionsAction.NAME, + clearVotingConfigExclusionsRequest, + new TransportResponseHandler() { + @Override + public void handleResponse(ClearVotingConfigExclusionsResponse response) { + logger.info("successfully cleared voting config after decommissioning"); + } + + @Override + public void handleException(TransportException exp) { + logger.debug(new ParameterizedMessage( + "failure in clearing voting config exclusion after decommissioning"), exp); + } + + @Override + public String executor() { + return ThreadPool.Names.SAME; + } + + @Override + public ClearVotingConfigExclusionsResponse read(StreamInput in) throws IOException { + return new ClearVotingConfigExclusionsResponse(in); + } + } + ); + } + /** * Registers new decommissioned attribute metadata in the cluster state *

@@ -186,6 +222,10 @@ public void onFailure(String source, Exception e) { // TODO - should we modify logic of logging for ease of debugging? if (e instanceof DecommissionFailedException) { logger.error(() -> new ParameterizedMessage("failed to decommission attribute [{}]", decommissionAttribute.toString()), e); + } else if (e instanceof NotClusterManagerException) { + logger.info(() -> new ParameterizedMessage( + "cluster-manager updated while executing request for decommission attribute [{}]", + decommissionAttribute.toString()), e); } else { // could be due to on longer cluster manager clusterService.submitStateUpdateTask( @@ -263,7 +303,9 @@ private void failDecommissionedNodes(ClusterState state) { nodesToBeDecommissioned.add(node); } } + // TODO - check for response from decommission request and then clear voting config? decommissionHelper.handleNodesDecommissionRequest(nodesToBeDecommissioned, "nodes-decommissioned"); + clearVotingConfigAfterSuccessfulDecommission(); } private static boolean nodeHasDecommissionedAttribute(DiscoveryNode discoveryNode, DecommissionAttribute decommissionAttribute) { From 87960b9dad0b377001b122dd4f85e4dfba7d500b Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 23 Aug 2022 14:35:57 +0530 Subject: [PATCH 121/187] Resolving comments Signed-off-by: Rishab Nahata --- .../org/opensearch/OpenSearchException.java | 8 +- .../org/opensearch/cluster/ClusterModule.java | 7 +- .../coordination/JoinTaskExecutor.java | 8 +- .../decommission/DecommissionAttribute.java | 14 +- .../DecommissionFailedException.java | 6 + .../decommission/DecommissionHelper.java | 18 +-- .../decommission/DecommissionService.java | 123 +++++++++++------- .../decommission/DecommissionStatus.java | 78 +++++------ .../NodeDecommissionedException.java | 1 - .../DecommissionAttributeMetadata.java | 54 +++----- 10 files changed, 161 insertions(+), 156 deletions(-) diff --git a/server/src/main/java/org/opensearch/OpenSearchException.java b/server/src/main/java/org/opensearch/OpenSearchException.java index 44d17cbca5652..5ae83c9df70d3 100644 --- a/server/src/main/java/org/opensearch/OpenSearchException.java +++ b/server/src/main/java/org/opensearch/OpenSearchException.java @@ -34,7 +34,6 @@ import org.opensearch.action.support.replication.ReplicationOperation; import org.opensearch.cluster.action.shard.ShardStateAction; -import org.opensearch.cluster.decommission.DecommissionFailedException; import org.opensearch.common.CheckedFunction; import org.opensearch.common.Nullable; import org.opensearch.common.ParseField; @@ -69,6 +68,7 @@ import static java.util.Collections.singletonMap; import static java.util.Collections.unmodifiableMap; import static org.opensearch.Version.V_2_1_0; +import static org.opensearch.Version.V_2_3_0; import static org.opensearch.Version.V_3_0_0; import static org.opensearch.cluster.metadata.IndexMetadata.INDEX_UUID_NA_VALUE; import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken; @@ -1614,13 +1614,13 @@ private enum OpenSearchExceptionHandle { org.opensearch.cluster.decommission.DecommissionFailedException.class, org.opensearch.cluster.decommission.DecommissionFailedException::new, 163, - V_2_1_0 + V_2_3_0 ), NODE_DECOMMISSIONED_EXCEPTION( org.opensearch.cluster.decommission.NodeDecommissionedException.class, org.opensearch.cluster.decommission.NodeDecommissionedException::new, - 163, - V_2_1_0 + 164, + V_2_3_0 ); final Class exceptionClass; diff --git a/server/src/main/java/org/opensearch/cluster/ClusterModule.java b/server/src/main/java/org/opensearch/cluster/ClusterModule.java index de63369dafc89..115b9bdf3d8d6 100644 --- a/server/src/main/java/org/opensearch/cluster/ClusterModule.java +++ b/server/src/main/java/org/opensearch/cluster/ClusterModule.java @@ -192,7 +192,12 @@ public static List getNamedWriteables() { ComposableIndexTemplateMetadata::readDiffFrom ); registerMetadataCustom(entries, DataStreamMetadata.TYPE, DataStreamMetadata::new, DataStreamMetadata::readDiffFrom); - registerMetadataCustom(entries, DecommissionAttributeMetadata.TYPE, DecommissionAttributeMetadata::new, DecommissionAttributeMetadata::readDiffFrom); + registerMetadataCustom( + entries, + DecommissionAttributeMetadata.TYPE, + DecommissionAttributeMetadata::new, + DecommissionAttributeMetadata::readDiffFrom + ); // Task Status (not Diffable) entries.add(new Entry(Task.Status.class, PersistentTasksNodeService.Status.NAME, PersistentTasksNodeService.Status::new)); return entries; diff --git a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java index dca706c250a20..c309d2a3e06cc 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java @@ -110,9 +110,7 @@ public boolean isBecomeClusterManagerTask() { return reason.equals(BECOME_MASTER_TASK_REASON) || reason.equals(BECOME_CLUSTER_MANAGER_TASK_REASON); } - /** - * @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #isBecomeClusterManagerTask()} - */ + /** @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #isBecomeClusterManagerTask()} */ @Deprecated public boolean isBecomeMasterTask() { return isBecomeClusterManagerTask(); @@ -484,9 +482,7 @@ public static void ensureNodeNotDecommissioned(DiscoveryNode node, Metadata meta if (decommissionAttribute != null) { if (node.getAttributes().get(decommissionAttribute.attributeName()).equals(decommissionAttribute.attributeValue())) { throw new NodeDecommissionedException( - "node has decommissioned attribute [" - + decommissionAttribute.toString() - + "]." + "node [{}] has decommissioned attribute [{}].", node.getId(), decommissionAttribute.toString() ); } } diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java index 15c17ae4b7ae1..1eb6b488447a1 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java @@ -8,6 +8,7 @@ package org.opensearch.cluster.decommission; +import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.io.stream.Writeable; @@ -15,12 +16,17 @@ import java.io.IOException; import java.util.Objects; +/** + * {@link DecommissionAttribute} encapsulates information about decommissioned node attribute like attribute name, attribute value. + * + * @opensearch.internal + */ public final class DecommissionAttribute implements Writeable { private final String attributeName; private final String attributeValue; /** - * Update the attribute value for a given attribute name to decommission + * Construct new decommission attribute with updated value from a given decommission attribute * * @param decommissionAttribute current decommissioned attribute object * @param attributeValue attribute value to be updated with @@ -100,12 +106,8 @@ public int hashCode() { return Objects.hash(attributeName, attributeValue); } - @Override public String toString() { - return "DecommissionAttribute{" + - "attributeName='" + attributeName + '\'' + - ", attributeValue='" + attributeValue + '\'' + - '}'; + return "DecommissionAttribute{" + "attributeName='" + attributeName + '\'' + ", attributeValue='" + attributeValue + '\'' + '}'; } } diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionFailedException.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionFailedException.java index 3a611c2488779..3ba121dd90cee 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionFailedException.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionFailedException.java @@ -14,6 +14,12 @@ import java.io.IOException; +/** + * This exception is thrown whenever a failure occurs in decommission request @{@link DecommissionService} + * + * @opensearch.internal + */ + public class DecommissionFailedException extends OpenSearchException { private final DecommissionAttribute decommissionAttribute; diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java index d1eb17adc9747..ce7befacaef98 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java @@ -14,6 +14,7 @@ import org.opensearch.cluster.ClusterStateTaskListener; import org.opensearch.cluster.coordination.NodeRemovalClusterStateTaskExecutor; import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.routing.allocation.AllocationService; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Priority; @@ -21,6 +22,12 @@ import java.util.List; import java.util.Map; +/** + * Helper executor class to remove list of nodes from the cluster + * + * @opensearch.internal + */ + public class DecommissionHelper { private static final Logger logger = LogManager.getLogger(DecommissionHelper.class); @@ -28,20 +35,15 @@ public class DecommissionHelper { private final NodeRemovalClusterStateTaskExecutor nodeRemovalExecutor; private final ClusterService clusterService; - DecommissionHelper( - ClusterService clusterService, - NodeRemovalClusterStateTaskExecutor nodeRemovalClusterStateTaskExecutor - ) { - this.nodeRemovalExecutor = nodeRemovalClusterStateTaskExecutor; + DecommissionHelper(ClusterService clusterService, AllocationService allocationService) { this.clusterService = clusterService; + this.nodeRemovalExecutor = new NodeRemovalClusterStateTaskExecutor(allocationService, logger); } public void handleNodesDecommissionRequest(List nodesToBeDecommissioned, String reason) { final Map nodesDecommissionTasks = new LinkedHashMap<>(); nodesToBeDecommissioned.forEach(discoveryNode -> { - final NodeRemovalClusterStateTaskExecutor.Task task = new NodeRemovalClusterStateTaskExecutor.Task( - discoveryNode, reason - ); + final NodeRemovalClusterStateTaskExecutor.Task task = new NodeRemovalClusterStateTaskExecutor.Task(discoveryNode, reason); nodesDecommissionTasks.put(task, nodeRemovalExecutor); }); final String source = "node-decommissioned"; diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 3971931180656..ee5e657a500ce 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -38,7 +38,6 @@ import org.opensearch.common.settings.Settings; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportException; -import org.opensearch.transport.TransportResponse; import org.opensearch.transport.TransportResponseHandler; import org.opensearch.transport.TransportService; @@ -48,13 +47,17 @@ import java.util.List; import java.util.function.Predicate; +/** + * Service responsible for entire lifecycle of decommissioning and recommissioning an awareness attribute. + * + * @opensearch.internal + */ public class DecommissionService implements ClusterStateApplier { private static final Logger logger = LogManager.getLogger(DecommissionService.class); private final ClusterService clusterService; private final TransportService transportService; - private final NodeRemovalClusterStateTaskExecutor nodeRemovalExecutor; private final ThreadPool threadPool; private final DecommissionHelper decommissionHelper; private ClusterState clusterState; @@ -72,11 +75,7 @@ public DecommissionService( this.clusterService = clusterService; this.transportService = transportService; this.threadPool = threadPool; - this.nodeRemovalExecutor = new NodeRemovalClusterStateTaskExecutor(allocationService, logger); - this.decommissionHelper = new DecommissionHelper( - clusterService, - nodeRemovalExecutor - ); + this.decommissionHelper = new DecommissionHelper(clusterService, allocationService); this.awarenessAttributes = AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING.get(settings); clusterSettings.addSettingsUpdateConsumer( AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING, @@ -113,7 +112,10 @@ private void excludeDecommissionedClusterManagerNodesFromVotingConfig( DecommissionAttribute decommissionAttribute, final ActionListener listener ) { - final Predicate shouldAbdicatePredicate = discoveryNode -> nodeHasDecommissionedAttribute(discoveryNode, decommissionAttribute); + final Predicate shouldAbdicatePredicate = discoveryNode -> nodeHasDecommissionedAttribute( + discoveryNode, + decommissionAttribute + ); List clusterManagerNodesToBeDecommissioned = new ArrayList<>(); Iterator clusterManagerNodesIter = clusterState.nodes().getClusterManagerNodes().valuesIt(); while (clusterManagerNodesIter.hasNext()) { @@ -129,15 +131,20 @@ private void excludeDecommissionedClusterManagerNodesFromVotingConfig( new TransportResponseHandler() { @Override public void handleResponse(AddVotingConfigExclusionsResponse response) { - logger.info("successfully removed decommissioned cluster manager eligible nodes [{}] from voting config, " + - "proceeding to drain the decommissioned nodes", clusterManagerNodesToBeDecommissioned.toString()); + logger.info( + "successfully removed decommissioned cluster manager eligible nodes [{}] from voting config, " + + "proceeding to drain the decommissioned nodes", + clusterManagerNodesToBeDecommissioned.toString() + ); registerDecommissionAttribute(decommissionAttribute, listener); } @Override public void handleException(TransportException exp) { - logger.debug(new ParameterizedMessage( - "failure in removing decommissioned cluster manager eligible nodes from voting config"), exp); + logger.debug( + new ParameterizedMessage("failure in removing decommissioned cluster manager eligible nodes from voting config"), + exp + ); } @Override @@ -168,8 +175,7 @@ public void handleResponse(ClearVotingConfigExclusionsResponse response) { @Override public void handleException(TransportException exp) { - logger.debug(new ParameterizedMessage( - "failure in clearing voting config exclusion after decommissioning"), exp); + logger.debug(new ParameterizedMessage("failure in clearing voting config exclusion after decommissioning"), exp); } @Override @@ -200,6 +206,10 @@ private void registerDecommissionAttribute( final DecommissionAttribute decommissionAttribute, final ActionListener listener ) { + // check the local node is master and not in decommission attribute + assert transportService.getLocalNode().isClusterManagerNode() + && !nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute) + : "cannot register decommission attribute, as local node is not master or is going to be decommissioned"; validateAwarenessAttribute(decommissionAttribute, getAwarenessAttributes()); clusterService.submitStateUpdateTask( "put_decommission [" + decommissionAttribute + "]", @@ -218,47 +228,57 @@ public ClusterState execute(ClusterState currentState) throws Exception { @Override public void onFailure(String source, Exception e) { - // TODO - should we put the weights back to zone, since we weighed away the zone before we started registering the metadata + // TODO - should we put the weights back to zone, since we weighed away the zone before we started registering the + // metadata // TODO - should we modify logic of logging for ease of debugging? if (e instanceof DecommissionFailedException) { - logger.error(() -> new ParameterizedMessage("failed to decommission attribute [{}]", decommissionAttribute.toString()), e); + logger.error( + () -> new ParameterizedMessage("failed to decommission attribute [{}]", decommissionAttribute.toString()), + e + ); } else if (e instanceof NotClusterManagerException) { - logger.info(() -> new ParameterizedMessage( - "cluster-manager updated while executing request for decommission attribute [{}]", - decommissionAttribute.toString()), e); + logger.info( + () -> new ParameterizedMessage( + "cluster-manager updated while executing request for decommission attribute [{}]", + decommissionAttribute.toString() + ), + e + ); } else { // could be due to on longer cluster manager - clusterService.submitStateUpdateTask( - "decommission_failed", - new ClusterStateUpdateTask(Priority.URGENT) { - @Override - public ClusterState execute(ClusterState currentState) throws Exception { - Metadata metadata = currentState.metadata(); - Metadata.Builder mdBuilder = Metadata.builder(metadata); - logger.info("decommission request for attribute [{}] failed", decommissionAttribute.toString()); - DecommissionAttributeMetadata decommissionAttributeMetadata = new DecommissionAttributeMetadata( - decommissionAttribute, - DecommissionStatus.DECOMMISSION_FAILED - ); - mdBuilder.putCustom(DecommissionAttributeMetadata.TYPE, decommissionAttributeMetadata); - return ClusterState.builder(currentState).metadata(mdBuilder).build(); - } + clusterService.submitStateUpdateTask("decommission_failed", new ClusterStateUpdateTask(Priority.URGENT) { + @Override + public ClusterState execute(ClusterState currentState) throws Exception { + Metadata metadata = currentState.metadata(); + Metadata.Builder mdBuilder = Metadata.builder(metadata); + logger.info("decommission request for attribute [{}] failed", decommissionAttribute.toString()); + DecommissionAttributeMetadata decommissionAttributeMetadata = new DecommissionAttributeMetadata( + decommissionAttribute, + DecommissionStatus.DECOMMISSION_FAILED + ); + mdBuilder.putCustom(DecommissionAttributeMetadata.TYPE, decommissionAttributeMetadata); + return ClusterState.builder(currentState).metadata(mdBuilder).build(); + } - @Override - public void onFailure(String source, Exception e) { - logger.error(() -> new ParameterizedMessage( + @Override + public void onFailure(String source, Exception e) { + logger.error( + () -> new ParameterizedMessage( "failed to mark status as DECOMMISSION_FAILED for decommission attribute [{}]", - decommissionAttribute.toString()), e); -// listener.onFailure(e); - } + decommissionAttribute.toString() + ), + e + ); + // listener.onFailure(e); } - ); + }); } listener.onFailure(e); } @Override public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + assert !newState.equals(oldState) : "no update in cluster state after initiating decommission request."; if (!newState.equals(oldState)) { // TODO - drain the nodes before decommissioning failDecommissionedNodes(newState); @@ -283,19 +303,26 @@ private static void ensureNoAwarenessAttributeDecommissioned( DecommissionAttribute decommissionAttribute ) { // If the previous decommission request failed, we will allow the request to pass this check - if (decommissionAttributeMetadata != null && !decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_FAILED)) { - throw new DecommissionFailedException(decommissionAttribute, "one awareness attribute already decommissioned, " + - "recommission before triggering another decommission"); + if (decommissionAttributeMetadata != null + && !decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_FAILED)) { + throw new DecommissionFailedException( + decommissionAttribute, + "one awareness attribute already decommissioned, " + "recommission before triggering another decommission" + ); } } private void failDecommissionedNodes(ClusterState state) { DecommissionAttributeMetadata decommissionAttributeMetadata = state.metadata().custom(DecommissionAttributeMetadata.TYPE); // TODO update the status check to DECOMMISSIONING once graceful decommission is implemented - assert decommissionAttributeMetadata.status().equals(DecommissionStatus.INIT) : "unexpected status encountered while decommissioning nodes"; + assert decommissionAttributeMetadata.status().equals(DecommissionStatus.INIT) + : "unexpected status encountered while decommissioning nodes"; DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute(); List nodesToBeDecommissioned = new ArrayList<>(); - final Predicate shouldRemoveNodePredicate = discoveryNode -> nodeHasDecommissionedAttribute(discoveryNode, decommissionAttribute); + final Predicate shouldRemoveNodePredicate = discoveryNode -> nodeHasDecommissionedAttribute( + discoveryNode, + decommissionAttribute + ); Iterator nodesIter = state.nodes().getNodes().valuesIt(); while (nodesIter.hasNext()) { final DiscoveryNode node = nodesIter.next(); @@ -309,9 +336,7 @@ private void failDecommissionedNodes(ClusterState state) { } private static boolean nodeHasDecommissionedAttribute(DiscoveryNode discoveryNode, DecommissionAttribute decommissionAttribute) { - return discoveryNode.getAttributes().get( - decommissionAttribute.attributeName() - ).equals(decommissionAttribute.attributeValue()); + return discoveryNode.getAttributes().get(decommissionAttribute.attributeName()).equals(decommissionAttribute.attributeValue()); } @Override diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java index 8f4ca3a6f578a..d091b8ab44e30 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java @@ -8,87 +8,73 @@ package org.opensearch.cluster.decommission; +/** + * An enumeration of the states during decommissioning and recommissioning. + */ public enum DecommissionStatus { /** * Decommission process is initiated */ - INIT((byte) 0), + INIT("init"), /** * Decommission process has started, decommissioned nodes should be weighed away */ - DECOMMISSIONING((byte) 1), + DECOMMISSION_IN_PROGRESS("decommission_in_progress"), /** * Decommissioning awareness attribute completed */ - DECOMMISSIONED((byte) 2), + DECOMMISSION_SUCCESSFUL("decommission_successful"), /** * Decommission request failed */ - DECOMMISSION_FAILED((byte) 3), + DECOMMISSION_FAILED("decommission_failed"), /** * Recommission request received, recommissioning process has started */ - RECOMMISSIONING((byte) 4), + RECOMMISSION_IN_PROGRESS("recommission_in_progress"), /** * Recommission request failed. No nodes should fail to join the cluster with decommission exception */ - RECOMMISSION_FAILED((byte) 5); + RECOMMISSION_FAILED("recommission_failed"); - private final byte value; + private final String status; - DecommissionStatus(byte value) { - this.value = value; + DecommissionStatus(String status) { + this.status = status; } /** - * Returns code that represents the decommission state + * Returns status that represents the decommission state * - * @return code for the state + * @return status */ - public byte value() { - return value; + public String status() { + return status; } /** - * Generate decommission state from code + * Generate decommission status from given string * - * @param value the state code - * @return state + * @param status status in string + * @return status */ - public static DecommissionStatus fromValue(byte value) { - switch (value) { - case 0: - return INIT; - case 1: - return DECOMMISSIONING; - case 2: - return DECOMMISSIONED; - case 3: - return DECOMMISSION_FAILED; - case 4: - return RECOMMISSIONING; - case 5: - return RECOMMISSION_FAILED; - default: - throw new IllegalArgumentException("No decommission state for value [" + value + "]"); - } - } - public static DecommissionStatus fromString(String status) { - if ("init".equals(status)) { + if (status == null) { + throw new IllegalArgumentException("decommission status cannot be null"); + } + if (status.equals(INIT.status())) { return INIT; - } else if ("decommissioning".equals(status)) { - return DECOMMISSIONING; - } else if ("decommissioned".equals(status)) { - return DECOMMISSIONED; - } else if ("decommission_failed".equals(status)) { + } else if (status.equals(DECOMMISSION_IN_PROGRESS.status())) { + return DECOMMISSION_IN_PROGRESS; + } else if (status.equals(DECOMMISSION_SUCCESSFUL.status())) { + return DECOMMISSION_SUCCESSFUL; + } else if (status.equals(DECOMMISSION_FAILED.status())) { return DECOMMISSION_FAILED; - } else if ("recommissioning".equals(status)) { - return RECOMMISSIONING; - } else if ("recommission_failed".equals(status)) { + } else if (status.equals(RECOMMISSION_IN_PROGRESS.status())) { + return RECOMMISSION_IN_PROGRESS; + } else if (status.equals(RECOMMISSION_FAILED.status())) { return RECOMMISSION_FAILED; } - throw new IllegalStateException("No status match for [" + status + "]"); + throw new IllegalStateException("Decommission status [" + status + "] not recognized."); } } - diff --git a/server/src/main/java/org/opensearch/cluster/decommission/NodeDecommissionedException.java b/server/src/main/java/org/opensearch/cluster/decommission/NodeDecommissionedException.java index d4ca4679a0872..847d5a527b017 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/NodeDecommissionedException.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/NodeDecommissionedException.java @@ -19,7 +19,6 @@ * * @opensearch.internal */ - public class NodeDecommissionedException extends OpenSearchException { public NodeDecommissionedException(String msg, Object... args) { diff --git a/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java b/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java index 869576f0ea070..258f2a4e1b2a8 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java @@ -57,8 +57,7 @@ public DecommissionAttributeMetadata(DecommissionAttribute decommissionAttribute * @param decommissionAttribute attribute details */ public DecommissionAttributeMetadata(DecommissionAttribute decommissionAttribute) { - this.decommissionAttribute = decommissionAttribute; - this.status = DecommissionStatus.INIT; + this(decommissionAttribute, DecommissionStatus.INIT); } /** @@ -79,13 +78,8 @@ public DecommissionStatus status() { return this.status; } - public DecommissionAttributeMetadata withUpdatedStatus( - DecommissionAttributeMetadata metadata, - DecommissionStatus status) { - return new DecommissionAttributeMetadata( - metadata.decommissionAttribute(), - status - ); + public DecommissionAttributeMetadata withUpdatedStatus(DecommissionAttributeMetadata metadata, DecommissionStatus status) { + return new DecommissionAttributeMetadata(metadata.decommissionAttribute(), status); } /** @@ -95,10 +89,7 @@ public DecommissionAttributeMetadata withUpdatedStatus( * @param attributeValue new attribute value * @return new instance with updated attribute value and status as DecommissionStatus.INIT */ - public DecommissionAttributeMetadata withUpdatedAttributeValue( - DecommissionAttributeMetadata metadata, - String attributeValue - ) { + public DecommissionAttributeMetadata withUpdatedAttributeValue(DecommissionAttributeMetadata metadata, String attributeValue) { return new DecommissionAttributeMetadata( new DecommissionAttribute(metadata.decommissionAttribute, attributeValue), DecommissionStatus.INIT @@ -116,21 +107,6 @@ public boolean equals(Object o) { return decommissionAttribute.equals(that.decommissionAttribute); } - /** - * Checks if this instance and the given instance share the same decommissioned attributeName - * and only differ in the attributeValue {@link DecommissionAttribute#attributeValue()} - * - * @param other other decommission attribute metadata - * @return {@code true} iff both instances contain the same attributeName - */ - public boolean equalsIgnoreValue(@Nullable DecommissionAttributeMetadata other) { - if (other == null) { - return false; - } - if (!status.equals(other.status)) return false; - return decommissionAttribute.equalsIgnoreValues(other.decommissionAttribute); - } - @Override public int hashCode() { return Objects.hash(attributeType, decommissionAttribute, status); @@ -146,12 +122,12 @@ public String getWriteableName() { @Override public Version getMinimalSupportedVersion() { - return Version.CURRENT.minimumCompatibilityVersion(); + return Version.V_2_3_0; } public DecommissionAttributeMetadata(StreamInput in) throws IOException { this.decommissionAttribute = new DecommissionAttribute(in); - this.status = DecommissionStatus.fromValue(in.readByte()); + this.status = DecommissionStatus.fromString(in.readString()); } public static NamedDiff readDiffFrom(StreamInput in) throws IOException { @@ -164,7 +140,7 @@ public static NamedDiff readDiffFrom(StreamInput in) throws IOException @Override public void writeTo(StreamOutput out) throws IOException { decommissionAttribute.writeTo(out); - out.writeByte(status.value()); + out.writeString(status.status()); } public static DecommissionAttributeMetadata fromXContent(XContentParser parser) throws IOException { @@ -176,7 +152,10 @@ public static DecommissionAttributeMetadata fromXContent(XContentParser parser) String currentFieldName = parser.currentName(); if (attributeType.equals(currentFieldName)) { if (parser.nextToken() != XContentParser.Token.START_OBJECT) { - throw new OpenSearchParseException("failed to parse decommission attribute type [{}], expected object", attributeType); + throw new OpenSearchParseException( + "failed to parse decommission attribute type [{}], expected object", + attributeType + ); } token = parser.nextToken(); if (token != XContentParser.Token.END_OBJECT) { @@ -187,7 +166,10 @@ public static DecommissionAttributeMetadata fromXContent(XContentParser parser) if (token == XContentParser.Token.VALUE_STRING) { value = parser.text(); } else { - throw new OpenSearchParseException("failed to parse attribute [{}], expected string for attribute value", fieldName); + throw new OpenSearchParseException( + "failed to parse attribute [{}], expected string for attribute value", + fieldName + ); } decommissionAttribute = new DecommissionAttribute(fieldName, value); } else { @@ -198,7 +180,9 @@ public static DecommissionAttributeMetadata fromXContent(XContentParser parser) } } else if ("status".equals(currentFieldName)) { if (parser.nextToken() != XContentParser.Token.VALUE_STRING) { - throw new OpenSearchParseException("failed to parse status of decommissioning, expected string but found unknown type"); + throw new OpenSearchParseException( + "failed to parse status of decommissioning, expected string but found unknown type" + ); } status = DecommissionStatus.fromString(parser.text()); } else { @@ -243,7 +227,7 @@ public static void toXContent( builder.startObject(attributeType); builder.field(decommissionAttribute.attributeName(), decommissionAttribute.attributeValue()); builder.endObject(); - builder.field("status", status); + builder.field("status", status.status()); } @Override From bb27b38de22c9431a1beadd6b386c50bdf7e790d Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 23 Aug 2022 15:24:43 +0530 Subject: [PATCH 122/187] Fixes Signed-off-by: Rishab Nahata --- .../decommission/DecommissionAttribute.java | 20 ------------------- .../decommission/DecommissionService.java | 7 ++++--- .../DecommissionAttributeMetadata.java | 14 ------------- 3 files changed, 4 insertions(+), 37 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java index 1eb6b488447a1..1a30b59d5a60e 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java @@ -25,16 +25,6 @@ public final class DecommissionAttribute implements Writeable { private final String attributeName; private final String attributeValue; - /** - * Construct new decommission attribute with updated value from a given decommission attribute - * - * @param decommissionAttribute current decommissioned attribute object - * @param attributeValue attribute value to be updated with - */ - public DecommissionAttribute(DecommissionAttribute decommissionAttribute, String attributeValue) { - this(decommissionAttribute.attributeName, attributeValue); - } - /** * Constructs new decommission attribute name value pair * @@ -80,16 +70,6 @@ public void writeTo(StreamOutput out) throws IOException { out.writeString(attributeValue); } - /** - * Checks if this instance is equal to the other instance in attributeName but differ in attribute value {@link #attributeValue}. - * - * @param other other decommission attribute name value - * @return {@code true} if both instances equal in attributeName fields but the attributeValue field - */ - public boolean equalsIgnoreValues(DecommissionAttribute other) { - return attributeName.equals(other.attributeName); - } - @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index ee5e657a500ce..53813ead9fcad 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -103,6 +103,7 @@ public void initiateAttributeDecommissioning( * 4. Once zone is weighed away -> trigger zone decommission using executor -> status should be set to DECOMMISSIONED on successful response * 5. Clear voting config */ + validateAwarenessAttribute(decommissionAttribute, getAwarenessAttributes()); this.clusterState = state; logger.info("initiating awareness attribute [{}] decommissioning", decommissionAttribute.toString()); excludeDecommissionedClusterManagerNodesFromVotingConfig(decommissionAttribute, listener); @@ -112,7 +113,7 @@ private void excludeDecommissionedClusterManagerNodesFromVotingConfig( DecommissionAttribute decommissionAttribute, final ActionListener listener ) { - final Predicate shouldAbdicatePredicate = discoveryNode -> nodeHasDecommissionedAttribute( + final Predicate shouldDecommissionPredicate = discoveryNode -> nodeHasDecommissionedAttribute( discoveryNode, decommissionAttribute ); @@ -120,7 +121,7 @@ private void excludeDecommissionedClusterManagerNodesFromVotingConfig( Iterator clusterManagerNodesIter = clusterState.nodes().getClusterManagerNodes().valuesIt(); while (clusterManagerNodesIter.hasNext()) { final DiscoveryNode node = clusterManagerNodesIter.next(); - if (shouldAbdicatePredicate.test(node)) { + if (shouldDecommissionPredicate.test(node)) { clusterManagerNodesToBeDecommissioned.add(node.getName()); } } @@ -210,7 +211,7 @@ private void registerDecommissionAttribute( assert transportService.getLocalNode().isClusterManagerNode() && !nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute) : "cannot register decommission attribute, as local node is not master or is going to be decommissioned"; - validateAwarenessAttribute(decommissionAttribute, getAwarenessAttributes()); + clusterService.submitStateUpdateTask( "put_decommission [" + decommissionAttribute + "]", new ClusterStateUpdateTask(Priority.URGENT) { diff --git a/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java b/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java index 258f2a4e1b2a8..6cba47d229534 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java @@ -82,20 +82,6 @@ public DecommissionAttributeMetadata withUpdatedStatus(DecommissionAttributeMeta return new DecommissionAttributeMetadata(metadata.decommissionAttribute(), status); } - /** - * Creates a new instance with a updated attribute value. - * - * @param metadata current metadata - * @param attributeValue new attribute value - * @return new instance with updated attribute value and status as DecommissionStatus.INIT - */ - public DecommissionAttributeMetadata withUpdatedAttributeValue(DecommissionAttributeMetadata metadata, String attributeValue) { - return new DecommissionAttributeMetadata( - new DecommissionAttribute(metadata.decommissionAttribute, attributeValue), - DecommissionStatus.INIT - ); - } - @Override public boolean equals(Object o) { if (this == o) return true; From b6b7b0df7c874d8a1381ce081fdba4da86f8ca7a Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 23 Aug 2022 16:46:33 +0530 Subject: [PATCH 123/187] Fixes Signed-off-by: Rishab Nahata --- .../decommission/DecommissionService.java | 55 ++++++++++++++++--- .../decommission/DecommissionStatus.java | 6 +- .../DecommissionAttributeMetadata.java | 14 +++-- 3 files changed, 58 insertions(+), 17 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 53813ead9fcad..fe67723187eab 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -24,7 +24,6 @@ import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.NotClusterManagerException; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; -import org.opensearch.cluster.coordination.NodeRemovalClusterStateTaskExecutor; import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; @@ -50,6 +49,17 @@ /** * Service responsible for entire lifecycle of decommissioning and recommissioning an awareness attribute. * + * Whenever a cluster manager initiates operation to decommission an awareness attribute, + * the service makes the best attempt to perform the following task - + *

+ * 1. Remove cluster-manager eligible nodes from voting config [TODO - checks to avoid quorum loss scenarios] + * 2. Initiates nodes decommissioning by adding custom metadata with the attribute and state as {@link DecommissionStatus#DECOMMISSION_INIT} + * 3. Triggers weigh away for nodes having given awareness attribute to drain. This marks the decommission status as {@link DecommissionStatus#DECOMMISSION_IN_PROGRESS} + * 4. Once weighed away, the service triggers nodes decommission + * 5. Once the decommission is successful, the service clears the voting config and marks the status as {@link DecommissionStatus#DECOMMISSION_SUCCESSFUL} + * 6. If service fails at any step, it would mark the status as {@link DecommissionStatus#DECOMMISSION_FAILED} + *

+ * * @opensearch.internal */ public class DecommissionService implements ClusterStateApplier { @@ -96,13 +106,6 @@ public void initiateAttributeDecommissioning( final ActionListener listener, ClusterState state ) { - /* - * 1. Abdicate master - * 2. Register attribute -> status should be set to INIT - * 3. Trigger weigh away for graceful decommission -> status should be set to DECOMMISSIONING - * 4. Once zone is weighed away -> trigger zone decommission using executor -> status should be set to DECOMMISSIONED on successful response - * 5. Clear voting config - */ validateAwarenessAttribute(decommissionAttribute, getAwarenessAttributes()); this.clusterState = state; logger.info("initiating awareness attribute [{}] decommissioning", decommissionAttribute.toString()); @@ -292,6 +295,40 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS ); } + // To Do - Can we add a consumer here such that whenever this succeeds we call the next method in on cluster state processed + private void updateMetadataWithDecommissionStatus( + DecommissionStatus decommissionStatus + ) { + clusterService.submitStateUpdateTask( + decommissionStatus.status(), + new ClusterStateUpdateTask(Priority.URGENT) { + @Override + public ClusterState execute(ClusterState currentState) throws Exception { + Metadata metadata = currentState.metadata(); + DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.custom(DecommissionAttributeMetadata.TYPE); + assert decommissionAttributeMetadata != null + && decommissionAttributeMetadata.decommissionAttribute() != null + : "failed to update status for decommission. metadata doesn't exist or invalid"; + Metadata.Builder mdBuilder = Metadata.builder(metadata); + DecommissionAttributeMetadata newMetadata = decommissionAttributeMetadata.withUpdatedStatus(decommissionStatus); + mdBuilder.putCustom(DecommissionAttributeMetadata.TYPE, newMetadata); + return ClusterState.builder(currentState).metadata(mdBuilder).build(); + } + + @Override + public void onFailure(String source, Exception e) { + logger.error( + () -> new ParameterizedMessage( + "failed to mark status as [{}]", + decommissionStatus.status() + ), + e + ); + } + } + ); + } + private static void validateAwarenessAttribute(final DecommissionAttribute decommissionAttribute, List awarenessAttributes) { if (!awarenessAttributes.contains(decommissionAttribute.attributeName())) { throw new DecommissionFailedException(decommissionAttribute, "invalid awareness attribute requested for decommissioning"); @@ -316,7 +353,7 @@ private static void ensureNoAwarenessAttributeDecommissioned( private void failDecommissionedNodes(ClusterState state) { DecommissionAttributeMetadata decommissionAttributeMetadata = state.metadata().custom(DecommissionAttributeMetadata.TYPE); // TODO update the status check to DECOMMISSIONING once graceful decommission is implemented - assert decommissionAttributeMetadata.status().equals(DecommissionStatus.INIT) + assert decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_INIT) : "unexpected status encountered while decommissioning nodes"; DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute(); List nodesToBeDecommissioned = new ArrayList<>(); diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java index d091b8ab44e30..41f9acfbc35d7 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java @@ -15,7 +15,7 @@ public enum DecommissionStatus { /** * Decommission process is initiated */ - INIT("init"), + DECOMMISSION_INIT("decommission_init"), /** * Decommission process has started, decommissioned nodes should be weighed away */ @@ -62,8 +62,8 @@ public static DecommissionStatus fromString(String status) { if (status == null) { throw new IllegalArgumentException("decommission status cannot be null"); } - if (status.equals(INIT.status())) { - return INIT; + if (status.equals(DECOMMISSION_INIT.status())) { + return DECOMMISSION_INIT; } else if (status.equals(DECOMMISSION_IN_PROGRESS.status())) { return DECOMMISSION_IN_PROGRESS; } else if (status.equals(DECOMMISSION_SUCCESSFUL.status())) { diff --git a/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java b/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java index 6cba47d229534..c6252e9981810 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java @@ -15,7 +15,6 @@ import org.opensearch.cluster.decommission.DecommissionAttribute; import org.opensearch.cluster.decommission.DecommissionStatus; import org.opensearch.cluster.metadata.Metadata.Custom; -import org.opensearch.common.Nullable; import org.opensearch.common.Strings; import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.io.stream.StreamOutput; @@ -52,12 +51,12 @@ public DecommissionAttributeMetadata(DecommissionAttribute decommissionAttribute } /** - * Constructs new decommission attribute metadata with status as {@link DecommissionStatus#INIT} + * Constructs new decommission attribute metadata with status as {@link DecommissionStatus#DECOMMISSION_INIT} * * @param decommissionAttribute attribute details */ public DecommissionAttributeMetadata(DecommissionAttribute decommissionAttribute) { - this(decommissionAttribute, DecommissionStatus.INIT); + this(decommissionAttribute, DecommissionStatus.DECOMMISSION_INIT); } /** @@ -78,8 +77,13 @@ public DecommissionStatus status() { return this.status; } - public DecommissionAttributeMetadata withUpdatedStatus(DecommissionAttributeMetadata metadata, DecommissionStatus status) { - return new DecommissionAttributeMetadata(metadata.decommissionAttribute(), status); + /** + * Creates a new instance that has the given decommission attribute moved to the given @{@link DecommissionStatus} + * @param status status to be updated with + * @return new instance with updated status + */ + public DecommissionAttributeMetadata withUpdatedStatus(DecommissionStatus status) { + return new DecommissionAttributeMetadata(decommissionAttribute(), status); } @Override From 798975547809bfc1da55225dcb77ad716cc8abf7 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 23 Aug 2022 18:07:09 +0530 Subject: [PATCH 124/187] Some refactpring Signed-off-by: Rishab Nahata --- .../decommission/DecommissionService.java | 76 ++++++++----------- 1 file changed, 31 insertions(+), 45 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index fe67723187eab..912fcda7baa62 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -21,6 +21,7 @@ import org.opensearch.cluster.ClusterChangedEvent; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateApplier; +import org.opensearch.cluster.ClusterStateTaskListener; import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.NotClusterManagerException; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; @@ -210,7 +211,6 @@ private void registerDecommissionAttribute( final DecommissionAttribute decommissionAttribute, final ActionListener listener ) { - // check the local node is master and not in decommission attribute assert transportService.getLocalNode().isClusterManagerNode() && !nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute) : "cannot register decommission attribute, as local node is not master or is going to be decommissioned"; @@ -219,8 +219,12 @@ private void registerDecommissionAttribute( "put_decommission [" + decommissionAttribute + "]", new ClusterStateUpdateTask(Priority.URGENT) { @Override - public ClusterState execute(ClusterState currentState) throws Exception { - logger.info("decommission request for attribute [{}] received", decommissionAttribute.toString()); + public ClusterState execute(ClusterState currentState) { + logger.info( + "registering decommission metadata for attribute [{}] with status as [{}]", + decommissionAttribute.toString(), + DecommissionStatus.DECOMMISSION_INIT + ); Metadata metadata = currentState.metadata(); Metadata.Builder mdBuilder = Metadata.builder(metadata); DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.custom(DecommissionAttributeMetadata.TYPE); @@ -232,69 +236,51 @@ public ClusterState execute(ClusterState currentState) throws Exception { @Override public void onFailure(String source, Exception e) { - // TODO - should we put the weights back to zone, since we weighed away the zone before we started registering the - // metadata - // TODO - should we modify logic of logging for ease of debugging? if (e instanceof DecommissionFailedException) { logger.error( - () -> new ParameterizedMessage("failed to decommission attribute [{}]", decommissionAttribute.toString()), + () -> new ParameterizedMessage( + "failed to decommission attribute [{}]", + decommissionAttribute.toString()), e ); + listener.onFailure(e); } else if (e instanceof NotClusterManagerException) { - logger.info( + logger.debug( () -> new ParameterizedMessage( "cluster-manager updated while executing request for decommission attribute [{}]", decommissionAttribute.toString() ), e ); + // Do we need a listener here as the transport request will be retried? } else { - // could be due to on longer cluster manager - clusterService.submitStateUpdateTask("decommission_failed", new ClusterStateUpdateTask(Priority.URGENT) { - @Override - public ClusterState execute(ClusterState currentState) throws Exception { - Metadata metadata = currentState.metadata(); - Metadata.Builder mdBuilder = Metadata.builder(metadata); - logger.info("decommission request for attribute [{}] failed", decommissionAttribute.toString()); - DecommissionAttributeMetadata decommissionAttributeMetadata = new DecommissionAttributeMetadata( - decommissionAttribute, - DecommissionStatus.DECOMMISSION_FAILED - ); - mdBuilder.putCustom(DecommissionAttributeMetadata.TYPE, decommissionAttributeMetadata); - return ClusterState.builder(currentState).metadata(mdBuilder).build(); - } - - @Override - public void onFailure(String source, Exception e) { - logger.error( - () -> new ParameterizedMessage( - "failed to mark status as DECOMMISSION_FAILED for decommission attribute [{}]", - decommissionAttribute.toString() - ), - e - ); - // listener.onFailure(e); - } - }); + logger.error( + () -> new ParameterizedMessage( + "failed to initiate decommissioning for attribute [{}]", + decommissionAttribute.toString() + ), + e + ); + updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_FAILED); + listener.onFailure(e); } - listener.onFailure(e); } @Override public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { assert !newState.equals(oldState) : "no update in cluster state after initiating decommission request."; - if (!newState.equals(oldState)) { - // TODO - drain the nodes before decommissioning - failDecommissionedNodes(newState); - listener.onResponse(new ClusterStateUpdateResponse(true)); - } else { - listener.onResponse(new ClusterStateUpdateResponse(false)); - } + // Do we attach a listener here with failed acknowledgement to the request? + listener.onResponse(new ClusterStateUpdateResponse(true)); + initiateGracefulDecommission(newState, listener); } } ); } + private void initiateGracefulDecommission(ClusterState clusterState, ActionListener listener) { + failDecommissionedNodes(clusterState, listener); + } + // To Do - Can we add a consumer here such that whenever this succeeds we call the next method in on cluster state processed private void updateMetadataWithDecommissionStatus( DecommissionStatus decommissionStatus @@ -345,12 +331,12 @@ private static void ensureNoAwarenessAttributeDecommissioned( && !decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_FAILED)) { throw new DecommissionFailedException( decommissionAttribute, - "one awareness attribute already decommissioned, " + "recommission before triggering another decommission" + "one awareness attribute already decommissioned, recommission before triggering another decommission" ); } } - private void failDecommissionedNodes(ClusterState state) { + private void failDecommissionedNodes(ClusterState state, ActionListener listener) { DecommissionAttributeMetadata decommissionAttributeMetadata = state.metadata().custom(DecommissionAttributeMetadata.TYPE); // TODO update the status check to DECOMMISSIONING once graceful decommission is implemented assert decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_INIT) From 0304491e118fe474ba3269aea0819eba067c6543 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 23 Aug 2022 19:35:36 +0530 Subject: [PATCH 125/187] Updates Signed-off-by: Rishab Nahata --- .../decommission/DecommissionService.java | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 912fcda7baa62..53e73f98e8590 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -21,7 +21,6 @@ import org.opensearch.cluster.ClusterChangedEvent; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateApplier; -import org.opensearch.cluster.ClusterStateTaskListener; import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.NotClusterManagerException; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; @@ -49,7 +48,7 @@ /** * Service responsible for entire lifecycle of decommissioning and recommissioning an awareness attribute. - * + *

* Whenever a cluster manager initiates operation to decommission an awareness attribute, * the service makes the best attempt to perform the following task - *

@@ -271,14 +270,14 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS assert !newState.equals(oldState) : "no update in cluster state after initiating decommission request."; // Do we attach a listener here with failed acknowledgement to the request? listener.onResponse(new ClusterStateUpdateResponse(true)); - initiateGracefulDecommission(newState, listener); + initiateGracefulDecommission(newState); } } ); } - private void initiateGracefulDecommission(ClusterState clusterState, ActionListener listener) { - failDecommissionedNodes(clusterState, listener); + private void initiateGracefulDecommission(ClusterState clusterState) { + failDecommissionedNodes(clusterState); } // To Do - Can we add a consumer here such that whenever this succeeds we call the next method in on cluster state processed @@ -295,6 +294,7 @@ public ClusterState execute(ClusterState currentState) throws Exception { assert decommissionAttributeMetadata != null && decommissionAttributeMetadata.decommissionAttribute() != null : "failed to update status for decommission. metadata doesn't exist or invalid"; + assert assertIncrementalStatusOrFailed(decommissionAttributeMetadata.status(), decommissionStatus); Metadata.Builder mdBuilder = Metadata.builder(metadata); DecommissionAttributeMetadata newMetadata = decommissionAttributeMetadata.withUpdatedStatus(decommissionStatus); mdBuilder.putCustom(DecommissionAttributeMetadata.TYPE, newMetadata); @@ -336,7 +336,7 @@ private static void ensureNoAwarenessAttributeDecommissioned( } } - private void failDecommissionedNodes(ClusterState state, ActionListener listener) { + private void failDecommissionedNodes(ClusterState state) { DecommissionAttributeMetadata decommissionAttributeMetadata = state.metadata().custom(DecommissionAttributeMetadata.TYPE); // TODO update the status check to DECOMMISSIONING once graceful decommission is implemented assert decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_INIT) @@ -359,6 +359,16 @@ private void failDecommissionedNodes(ClusterState state, ActionListener Date: Wed, 24 Aug 2022 00:50:27 +0530 Subject: [PATCH 126/187] Fix to abdication Signed-off-by: Rishab Nahata --- .../decommission/DecommissionService.java | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 53e73f98e8590..246012d9339bf 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -109,12 +109,12 @@ public void initiateAttributeDecommissioning( validateAwarenessAttribute(decommissionAttribute, getAwarenessAttributes()); this.clusterState = state; logger.info("initiating awareness attribute [{}] decommissioning", decommissionAttribute.toString()); - excludeDecommissionedClusterManagerNodesFromVotingConfig(decommissionAttribute, listener); + excludeDecommissionedClusterManagerNodesFromVotingConfig(decommissionAttribute); + registerDecommissionAttribute(decommissionAttribute, listener); } private void excludeDecommissionedClusterManagerNodesFromVotingConfig( - DecommissionAttribute decommissionAttribute, - final ActionListener listener + DecommissionAttribute decommissionAttribute ) { final Predicate shouldDecommissionPredicate = discoveryNode -> nodeHasDecommissionedAttribute( discoveryNode, @@ -140,7 +140,6 @@ public void handleResponse(AddVotingConfigExclusionsResponse response) { + "proceeding to drain the decommissioned nodes", clusterManagerNodesToBeDecommissioned.toString() ); - registerDecommissionAttribute(decommissionAttribute, listener); } @Override @@ -210,10 +209,14 @@ private void registerDecommissionAttribute( final DecommissionAttribute decommissionAttribute, final ActionListener listener ) { - assert transportService.getLocalNode().isClusterManagerNode() - && !nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute) - : "cannot register decommission attribute, as local node is not master or is going to be decommissioned"; - + logger.info("Node is - " + transportService.getLocalNode()); + if (!transportService.getLocalNode().isClusterManagerNode() + || nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute)) + { + throw new NotClusterManagerException( + "Node [" + transportService.getLocalNode() + "] not eligible to execute decommission request" + ); + } clusterService.submitStateUpdateTask( "put_decommission [" + decommissionAttribute + "]", new ClusterStateUpdateTask(Priority.URGENT) { From 72e76b3ddcbb6ab2d5cd69b6adf0445879b72d87 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Wed, 24 Aug 2022 01:17:37 +0530 Subject: [PATCH 127/187] Remove cluster state variable from service Signed-off-by: Rishab Nahata --- .../cluster/decommission/DecommissionService.java | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 246012d9339bf..b3fcd9c04a15c 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -62,7 +62,7 @@ * * @opensearch.internal */ -public class DecommissionService implements ClusterStateApplier { +public class DecommissionService { private static final Logger logger = LogManager.getLogger(DecommissionService.class); @@ -70,7 +70,6 @@ public class DecommissionService implements ClusterStateApplier { private final TransportService transportService; private final ThreadPool threadPool; private final DecommissionHelper decommissionHelper; - private ClusterState clusterState; private volatile List awarenessAttributes; @Inject @@ -107,7 +106,6 @@ public void initiateAttributeDecommissioning( ClusterState state ) { validateAwarenessAttribute(decommissionAttribute, getAwarenessAttributes()); - this.clusterState = state; logger.info("initiating awareness attribute [{}] decommissioning", decommissionAttribute.toString()); excludeDecommissionedClusterManagerNodesFromVotingConfig(decommissionAttribute); registerDecommissionAttribute(decommissionAttribute, listener); @@ -121,7 +119,7 @@ private void excludeDecommissionedClusterManagerNodesFromVotingConfig( decommissionAttribute ); List clusterManagerNodesToBeDecommissioned = new ArrayList<>(); - Iterator clusterManagerNodesIter = clusterState.nodes().getClusterManagerNodes().valuesIt(); + Iterator clusterManagerNodesIter = clusterService.state().nodes().getClusterManagerNodes().valuesIt(); while (clusterManagerNodesIter.hasNext()) { final DiscoveryNode node = clusterManagerNodesIter.next(); if (shouldDecommissionPredicate.test(node)) { @@ -209,7 +207,6 @@ private void registerDecommissionAttribute( final DecommissionAttribute decommissionAttribute, final ActionListener listener ) { - logger.info("Node is - " + transportService.getLocalNode()); if (!transportService.getLocalNode().isClusterManagerNode() || nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute)) { @@ -375,9 +372,4 @@ else if (newStatus.equals(DecommissionStatus.DECOMMISSION_SUCCESSFUL)) { private static boolean nodeHasDecommissionedAttribute(DiscoveryNode discoveryNode, DecommissionAttribute decommissionAttribute) { return discoveryNode.getAttributes().get(decommissionAttribute.attributeName()).equals(decommissionAttribute.attributeValue()); } - - @Override - public void applyClusterState(ClusterChangedEvent event) { - clusterState = event.state(); - } } From 586172ac8b0f5b9f04d9af6629052043d5388731 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Wed, 24 Aug 2022 01:26:01 +0530 Subject: [PATCH 128/187] Log node string Signed-off-by: Rishab Nahata --- .../org/opensearch/cluster/coordination/JoinTaskExecutor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java index c309d2a3e06cc..997ddc3da9ea6 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java @@ -482,7 +482,7 @@ public static void ensureNodeNotDecommissioned(DiscoveryNode node, Metadata meta if (decommissionAttribute != null) { if (node.getAttributes().get(decommissionAttribute.attributeName()).equals(decommissionAttribute.attributeValue())) { throw new NodeDecommissionedException( - "node [{}] has decommissioned attribute [{}].", node.getId(), decommissionAttribute.toString() + "node [{}] has decommissioned attribute [{}].", node.toString(), decommissionAttribute.toString() ); } } From 44540a5a5d37d047e6255c2845207e9001a5b287 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Wed, 24 Aug 2022 12:11:32 +0530 Subject: [PATCH 129/187] Fix conflict Signed-off-by: Rishab Nahata --- .../decommission/DecommissionService.java | 100 +++++++++++++++--- 1 file changed, 85 insertions(+), 15 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index b3fcd9c04a15c..87081d2ee38b1 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -11,6 +11,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; +import org.opensearch.OpenSearchException; import org.opensearch.action.ActionListener; import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsAction; import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsRequest; @@ -21,6 +22,7 @@ import org.opensearch.cluster.ClusterChangedEvent; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateApplier; +import org.opensearch.cluster.ClusterStateObserver; import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.NotClusterManagerException; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; @@ -35,6 +37,7 @@ import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Settings; +import org.opensearch.common.unit.TimeValue; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportException; import org.opensearch.transport.TransportResponseHandler; @@ -211,7 +214,7 @@ private void registerDecommissionAttribute( || nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute)) { throw new NotClusterManagerException( - "Node [" + transportService.getLocalNode() + "] not eligible to execute decommission request" + "node [" + transportService.getLocalNode().toString() + "] not eligible to execute decommission request" ); } clusterService.submitStateUpdateTask( @@ -251,7 +254,6 @@ public void onFailure(String source, Exception e) { ), e ); - // Do we need a listener here as the transport request will be retried? } else { logger.error( () -> new ParameterizedMessage( @@ -260,7 +262,7 @@ public void onFailure(String source, Exception e) { ), e ); - updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_FAILED); +// updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_FAILED); listener.onFailure(e); } } @@ -270,19 +272,32 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS assert !newState.equals(oldState) : "no update in cluster state after initiating decommission request."; // Do we attach a listener here with failed acknowledgement to the request? listener.onResponse(new ClusterStateUpdateResponse(true)); - initiateGracefulDecommission(newState); + initiateGracefulDecommission(); } } ); } - private void initiateGracefulDecommission(ClusterState clusterState) { - failDecommissionedNodes(clusterState); + private void initiateGracefulDecommission() { + ActionListener listener = new ActionListener() { + @Override + public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { + failDecommissionedNodes(clusterService.state()); + } + + @Override + public void onFailure(Exception e) { + + } + }; + updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_IN_PROGRESS, listener); + //TODO - code for graceful decommission } // To Do - Can we add a consumer here such that whenever this succeeds we call the next method in on cluster state processed private void updateMetadataWithDecommissionStatus( - DecommissionStatus decommissionStatus + DecommissionStatus decommissionStatus, + ActionListener listener ) { clusterService.submitStateUpdateTask( decommissionStatus.status(), @@ -311,6 +326,13 @@ public void onFailure(String source, Exception e) { e ); } + + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + listener.onResponse(new ClusterStateUpdateResponse(true)); + } + + } ); } @@ -338,25 +360,73 @@ private static void ensureNoAwarenessAttributeDecommissioned( private void failDecommissionedNodes(ClusterState state) { DecommissionAttributeMetadata decommissionAttributeMetadata = state.metadata().custom(DecommissionAttributeMetadata.TYPE); - // TODO update the status check to DECOMMISSIONING once graceful decommission is implemented - assert decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_INIT) + assert decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_IN_PROGRESS) : "unexpected status encountered while decommissioning nodes"; DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute(); - List nodesToBeDecommissioned = new ArrayList<>(); + + decommissionHelper.handleNodesDecommissionRequest( + nodesWithDecommissionAttribute(state, decommissionAttribute), + "nodes-decommissioned" + ); + + final ClusterStateObserver observer = new ClusterStateObserver( + clusterService, + TimeValue.timeValueSeconds(30L), + logger, + threadPool.getThreadContext() + ); + + final Predicate allDecommissionedNodesRemoved = clusterState -> { + List nodesWithDecommissionAttribute = nodesWithDecommissionAttribute(clusterState, decommissionAttribute); + return nodesWithDecommissionAttribute.size() == 0; + }; + ActionListener statusUpdateListener = new ActionListener() { + @Override + public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { + logger.info( + "successfully updated decommission status" + ); + } + + @Override + public void onFailure(Exception e) { + + } + }; + + observer.waitForNextChange(new ClusterStateObserver.Listener() { + @Override + public void onNewClusterState(ClusterState state) { + clearVotingConfigAfterSuccessfulDecommission(); + updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_SUCCESSFUL, statusUpdateListener); + } + + @Override + public void onClusterServiceClose() { + } + + @Override + public void onTimeout(TimeValue timeout) { + clearVotingConfigAfterSuccessfulDecommission(); + updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_FAILED, statusUpdateListener); + } + }, allDecommissionedNodesRemoved, TimeValue.timeValueSeconds(30L)); + } + + private List nodesWithDecommissionAttribute(ClusterState clusterState, DecommissionAttribute decommissionAttribute) { + List nodesWithDecommissionAttribute = new ArrayList<>(); final Predicate shouldRemoveNodePredicate = discoveryNode -> nodeHasDecommissionedAttribute( discoveryNode, decommissionAttribute ); - Iterator nodesIter = state.nodes().getNodes().valuesIt(); + Iterator nodesIter = clusterState.nodes().getNodes().valuesIt(); while (nodesIter.hasNext()) { final DiscoveryNode node = nodesIter.next(); if (shouldRemoveNodePredicate.test(node)) { - nodesToBeDecommissioned.add(node); + nodesWithDecommissionAttribute.add(node); } } - // TODO - check for response from decommission request and then clear voting config? - decommissionHelper.handleNodesDecommissionRequest(nodesToBeDecommissioned, "nodes-decommissioned"); - clearVotingConfigAfterSuccessfulDecommission(); + return nodesWithDecommissionAttribute; } private static boolean assertIncrementalStatusOrFailed(DecommissionStatus oldStatus, DecommissionStatus newStatus) { From 9d417af589d5d4258785d8b3ab728ca02c186d6f Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Wed, 24 Aug 2022 15:50:23 +0530 Subject: [PATCH 130/187] Changes in Service Signed-off-by: Rishab Nahata --- .../decommission/DecommissionService.java | 207 +++++++++--------- 1 file changed, 106 insertions(+), 101 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 87081d2ee38b1..b97cca12c5006 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -11,7 +11,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.opensearch.OpenSearchException; import org.opensearch.action.ActionListener; import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsAction; import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsRequest; @@ -19,9 +18,7 @@ import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsAction; import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsRequest; import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsResponse; -import org.opensearch.cluster.ClusterChangedEvent; import org.opensearch.cluster.ClusterState; -import org.opensearch.cluster.ClusterStateApplier; import org.opensearch.cluster.ClusterStateObserver; import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.NotClusterManagerException; @@ -54,14 +51,14 @@ *

* Whenever a cluster manager initiates operation to decommission an awareness attribute, * the service makes the best attempt to perform the following task - - *

- * 1. Remove cluster-manager eligible nodes from voting config [TODO - checks to avoid quorum loss scenarios] - * 2. Initiates nodes decommissioning by adding custom metadata with the attribute and state as {@link DecommissionStatus#DECOMMISSION_INIT} - * 3. Triggers weigh away for nodes having given awareness attribute to drain. This marks the decommission status as {@link DecommissionStatus#DECOMMISSION_IN_PROGRESS} - * 4. Once weighed away, the service triggers nodes decommission - * 5. Once the decommission is successful, the service clears the voting config and marks the status as {@link DecommissionStatus#DECOMMISSION_SUCCESSFUL} - * 6. If service fails at any step, it would mark the status as {@link DecommissionStatus#DECOMMISSION_FAILED} - *

+ *
    + *
  • Remove cluster-manager eligible nodes from voting config [TODO - checks to avoid quorum loss scenarios]
  • + *
  • Initiates nodes decommissioning by adding custom metadata with the attribute and state as {@link DecommissionStatus#DECOMMISSION_INIT}
  • + *
  • Triggers weigh away for nodes having given awareness attribute to drain. This marks the decommission status as {@link DecommissionStatus#DECOMMISSION_IN_PROGRESS}
  • + *
  • Once weighed away, the service triggers nodes decommission
  • + *
  • Once the decommission is successful, the service clears the voting config and marks the status as {@link DecommissionStatus#DECOMMISSION_SUCCESSFUL}
  • + *
  • If service fails at any step, it would mark the status as {@link DecommissionStatus#DECOMMISSION_FAILED}
  • + *
* * @opensearch.internal */ @@ -114,9 +111,7 @@ public void initiateAttributeDecommissioning( registerDecommissionAttribute(decommissionAttribute, listener); } - private void excludeDecommissionedClusterManagerNodesFromVotingConfig( - DecommissionAttribute decommissionAttribute - ) { + private void excludeDecommissionedClusterManagerNodesFromVotingConfig(DecommissionAttribute decommissionAttribute) { final Predicate shouldDecommissionPredicate = discoveryNode -> nodeHasDecommissionedAttribute( discoveryNode, decommissionAttribute @@ -196,12 +191,12 @@ public ClearVotingConfigExclusionsResponse read(StreamInput in) throws IOExcepti } /** - * Registers new decommissioned attribute metadata in the cluster state + * Registers new decommissioned attribute metadata in the cluster state with {@link DecommissionStatus#DECOMMISSION_INIT} *

* This method can be only called on the cluster-manager node. It tries to create a new decommissioned attribute on the master * and if it was successful it adds new decommissioned attribute to cluster metadata. *

- * This method should only be called once the eligible cluster manager node having decommissioned attribute is abdicated + * This method ensures that request is performed only on eligible cluster manager node * * @param decommissionAttribute register decommission attribute in the metadata request * @param listener register decommission listener @@ -210,11 +205,10 @@ private void registerDecommissionAttribute( final DecommissionAttribute decommissionAttribute, final ActionListener listener ) { - if (!transportService.getLocalNode().isClusterManagerNode() - || nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute)) + if (!transportService.getLocalNode().isClusterManagerNode() || nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute)) { throw new NotClusterManagerException( - "node [" + transportService.getLocalNode().toString() + "] not eligible to execute decommission request" + "node [" + transportService.getLocalNode().toString() + "] not eligible to execute decommission request. Will retry until timeout." ); } clusterService.submitStateUpdateTask( @@ -262,15 +256,15 @@ public void onFailure(String source, Exception e) { ), e ); -// updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_FAILED); listener.onFailure(e); } } @Override public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { - assert !newState.equals(oldState) : "no update in cluster state after initiating decommission request."; - // Do we attach a listener here with failed acknowledgement to the request? + DecommissionAttributeMetadata decommissionAttributeMetadata = newState.metadata().custom(DecommissionAttributeMetadata.TYPE); + assert decommissionAttribute.equals(decommissionAttributeMetadata.decommissionAttribute()); + assert DecommissionStatus.DECOMMISSION_INIT.equals(decommissionAttributeMetadata.status()); listener.onResponse(new ClusterStateUpdateResponse(true)); initiateGracefulDecommission(); } @@ -279,22 +273,88 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS } private void initiateGracefulDecommission() { - ActionListener listener = new ActionListener() { + // maybe create a supplier for status update listener? + ActionListener listener = new ActionListener<>() { @Override public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { + logger.info("updated decommission status to [{}], weighing away awareness attribute for graceful shutdown", + DecommissionStatus.DECOMMISSION_IN_PROGRESS); failDecommissionedNodes(clusterService.state()); } @Override public void onFailure(Exception e) { - + logger.error( + () -> new ParameterizedMessage( "failed to update decommission status to [{}], will not proceed with decommission" + , DecommissionStatus.DECOMMISSION_IN_PROGRESS), + e + ); } }; updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_IN_PROGRESS, listener); //TODO - code for graceful decommission } - // To Do - Can we add a consumer here such that whenever this succeeds we call the next method in on cluster state processed + private void failDecommissionedNodes(ClusterState state) { + DecommissionAttributeMetadata decommissionAttributeMetadata = state.metadata().custom(DecommissionAttributeMetadata.TYPE); + assert decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_IN_PROGRESS) + : "unexpected status encountered while decommissioning nodes"; + DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute(); + + ActionListener statusUpdateListener = new ActionListener<>() { + @Override + public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { + logger.info( + "successfully updated decommission status" + ); + } + + @Override + public void onFailure(Exception e) { + logger.error("failed to update the decommission status"); + } + }; + + // execute decommissioning + decommissionHelper.handleNodesDecommissionRequest( + nodesWithDecommissionAttribute(state, decommissionAttribute), + "nodes-decommissioned" + ); + + final ClusterStateObserver observer = new ClusterStateObserver( + clusterService, + TimeValue.timeValueSeconds(30L), // should this be a setting? + logger, + threadPool.getThreadContext() + ); + + final Predicate allDecommissionedNodesRemoved = clusterState -> { + List nodesWithDecommissionAttribute = nodesWithDecommissionAttribute(clusterState, decommissionAttribute); + return nodesWithDecommissionAttribute.size() == 0; + }; + + observer.waitForNextChange(new ClusterStateObserver.Listener() { + @Override + public void onNewClusterState(ClusterState state) { + logger.info("successfully removed all decommissioned nodes from the cluster"); + clearVotingConfigAfterSuccessfulDecommission(); + updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_SUCCESSFUL, statusUpdateListener); + } + + @Override + public void onClusterServiceClose() { + logger.debug("cluster service closed while waiting for removal of decommissioned nodes."); + } + + @Override + public void onTimeout(TimeValue timeout) { + logger.info("timed out while waiting for removal of decommissioned nodes"); + clearVotingConfigAfterSuccessfulDecommission(); + updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_FAILED, statusUpdateListener); + } + }, allDecommissionedNodesRemoved); + } + private void updateMetadataWithDecommissionStatus( DecommissionStatus decommissionStatus, ActionListener listener @@ -337,82 +397,6 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS ); } - private static void validateAwarenessAttribute(final DecommissionAttribute decommissionAttribute, List awarenessAttributes) { - if (!awarenessAttributes.contains(decommissionAttribute.attributeName())) { - throw new DecommissionFailedException(decommissionAttribute, "invalid awareness attribute requested for decommissioning"); - } - // TODO - should attribute value be part of force zone values? If yes, read setting and throw exception if not found - } - - private static void ensureNoAwarenessAttributeDecommissioned( - DecommissionAttributeMetadata decommissionAttributeMetadata, - DecommissionAttribute decommissionAttribute - ) { - // If the previous decommission request failed, we will allow the request to pass this check - if (decommissionAttributeMetadata != null - && !decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_FAILED)) { - throw new DecommissionFailedException( - decommissionAttribute, - "one awareness attribute already decommissioned, recommission before triggering another decommission" - ); - } - } - - private void failDecommissionedNodes(ClusterState state) { - DecommissionAttributeMetadata decommissionAttributeMetadata = state.metadata().custom(DecommissionAttributeMetadata.TYPE); - assert decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_IN_PROGRESS) - : "unexpected status encountered while decommissioning nodes"; - DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute(); - - decommissionHelper.handleNodesDecommissionRequest( - nodesWithDecommissionAttribute(state, decommissionAttribute), - "nodes-decommissioned" - ); - - final ClusterStateObserver observer = new ClusterStateObserver( - clusterService, - TimeValue.timeValueSeconds(30L), - logger, - threadPool.getThreadContext() - ); - - final Predicate allDecommissionedNodesRemoved = clusterState -> { - List nodesWithDecommissionAttribute = nodesWithDecommissionAttribute(clusterState, decommissionAttribute); - return nodesWithDecommissionAttribute.size() == 0; - }; - ActionListener statusUpdateListener = new ActionListener() { - @Override - public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { - logger.info( - "successfully updated decommission status" - ); - } - - @Override - public void onFailure(Exception e) { - - } - }; - - observer.waitForNextChange(new ClusterStateObserver.Listener() { - @Override - public void onNewClusterState(ClusterState state) { - clearVotingConfigAfterSuccessfulDecommission(); - updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_SUCCESSFUL, statusUpdateListener); - } - - @Override - public void onClusterServiceClose() { - } - - @Override - public void onTimeout(TimeValue timeout) { - clearVotingConfigAfterSuccessfulDecommission(); - updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_FAILED, statusUpdateListener); - } - }, allDecommissionedNodesRemoved, TimeValue.timeValueSeconds(30L)); - } - private List nodesWithDecommissionAttribute(ClusterState clusterState, DecommissionAttribute decommissionAttribute) { List nodesWithDecommissionAttribute = new ArrayList<>(); final Predicate shouldRemoveNodePredicate = discoveryNode -> nodeHasDecommissionedAttribute( @@ -442,4 +426,25 @@ else if (newStatus.equals(DecommissionStatus.DECOMMISSION_SUCCESSFUL)) { private static boolean nodeHasDecommissionedAttribute(DiscoveryNode discoveryNode, DecommissionAttribute decommissionAttribute) { return discoveryNode.getAttributes().get(decommissionAttribute.attributeName()).equals(decommissionAttribute.attributeValue()); } + + private static void validateAwarenessAttribute(final DecommissionAttribute decommissionAttribute, List awarenessAttributes) { + if (!awarenessAttributes.contains(decommissionAttribute.attributeName())) { + throw new DecommissionFailedException(decommissionAttribute, "invalid awareness attribute requested for decommissioning"); + } + // TODO - should attribute value be part of force zone values? If yes, read setting and throw exception if not found + } + + private static void ensureNoAwarenessAttributeDecommissioned( + DecommissionAttributeMetadata decommissionAttributeMetadata, + DecommissionAttribute decommissionAttribute + ) { + // If the previous decommission request failed, we will allow the request to pass this check + if (decommissionAttributeMetadata != null + && !decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_FAILED)) { + throw new DecommissionFailedException( + decommissionAttribute, + "one awareness attribute already decommissioned, recommission before triggering another decommission" + ); + } + } } From 5a0d0bfdfe8cf512811a7dd561408b9b71a6c64a Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Wed, 24 Aug 2022 15:55:34 +0530 Subject: [PATCH 131/187] Fix spotless check Signed-off-by: Rishab Nahata --- .../org/opensearch/OpenSearchException.java | 1 - .../coordination/JoinTaskExecutor.java | 4 +- .../decommission/DecommissionAttribute.java | 1 - .../decommission/DecommissionService.java | 90 +++++++++---------- 4 files changed, 44 insertions(+), 52 deletions(-) diff --git a/server/src/main/java/org/opensearch/OpenSearchException.java b/server/src/main/java/org/opensearch/OpenSearchException.java index 5ae83c9df70d3..b1e9a27b98416 100644 --- a/server/src/main/java/org/opensearch/OpenSearchException.java +++ b/server/src/main/java/org/opensearch/OpenSearchException.java @@ -69,7 +69,6 @@ import static java.util.Collections.unmodifiableMap; import static org.opensearch.Version.V_2_1_0; import static org.opensearch.Version.V_2_3_0; -import static org.opensearch.Version.V_3_0_0; import static org.opensearch.cluster.metadata.IndexMetadata.INDEX_UUID_NA_VALUE; import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken; import static org.opensearch.common.xcontent.XContentParserUtils.ensureFieldName; diff --git a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java index 997ddc3da9ea6..9825bebb63dd6 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java @@ -482,7 +482,9 @@ public static void ensureNodeNotDecommissioned(DiscoveryNode node, Metadata meta if (decommissionAttribute != null) { if (node.getAttributes().get(decommissionAttribute.attributeName()).equals(decommissionAttribute.attributeValue())) { throw new NodeDecommissionedException( - "node [{}] has decommissioned attribute [{}].", node.toString(), decommissionAttribute.toString() + "node [{}] has decommissioned attribute [{}].", + node.toString(), + decommissionAttribute.toString() ); } } diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java index 1a30b59d5a60e..bf2487a1a0e18 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java @@ -8,7 +8,6 @@ package org.opensearch.cluster.decommission; -import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.io.stream.Writeable; diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index b97cca12c5006..139b241ea00a4 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -205,10 +205,12 @@ private void registerDecommissionAttribute( final DecommissionAttribute decommissionAttribute, final ActionListener listener ) { - if (!transportService.getLocalNode().isClusterManagerNode() || nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute)) - { + if (!transportService.getLocalNode().isClusterManagerNode() + || nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute)) { throw new NotClusterManagerException( - "node [" + transportService.getLocalNode().toString() + "] not eligible to execute decommission request. Will retry until timeout." + "node [" + + transportService.getLocalNode().toString() + + "] not eligible to execute decommission request. Will retry until timeout." ); } clusterService.submitStateUpdateTask( @@ -234,9 +236,7 @@ public ClusterState execute(ClusterState currentState) { public void onFailure(String source, Exception e) { if (e instanceof DecommissionFailedException) { logger.error( - () -> new ParameterizedMessage( - "failed to decommission attribute [{}]", - decommissionAttribute.toString()), + () -> new ParameterizedMessage("failed to decommission attribute [{}]", decommissionAttribute.toString()), e ); listener.onFailure(e); @@ -262,7 +262,8 @@ public void onFailure(String source, Exception e) { @Override public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { - DecommissionAttributeMetadata decommissionAttributeMetadata = newState.metadata().custom(DecommissionAttributeMetadata.TYPE); + DecommissionAttributeMetadata decommissionAttributeMetadata = newState.metadata() + .custom(DecommissionAttributeMetadata.TYPE); assert decommissionAttribute.equals(decommissionAttributeMetadata.decommissionAttribute()); assert DecommissionStatus.DECOMMISSION_INIT.equals(decommissionAttributeMetadata.status()); listener.onResponse(new ClusterStateUpdateResponse(true)); @@ -277,22 +278,26 @@ private void initiateGracefulDecommission() { ActionListener listener = new ActionListener<>() { @Override public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { - logger.info("updated decommission status to [{}], weighing away awareness attribute for graceful shutdown", - DecommissionStatus.DECOMMISSION_IN_PROGRESS); + logger.info( + "updated decommission status to [{}], weighing away awareness attribute for graceful shutdown", + DecommissionStatus.DECOMMISSION_IN_PROGRESS + ); failDecommissionedNodes(clusterService.state()); } @Override public void onFailure(Exception e) { logger.error( - () -> new ParameterizedMessage( "failed to update decommission status to [{}], will not proceed with decommission" - , DecommissionStatus.DECOMMISSION_IN_PROGRESS), + () -> new ParameterizedMessage( + "failed to update decommission status to [{}], will not proceed with decommission", + DecommissionStatus.DECOMMISSION_IN_PROGRESS + ), e ); } }; updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_IN_PROGRESS, listener); - //TODO - code for graceful decommission + // TODO - code for graceful decommission } private void failDecommissionedNodes(ClusterState state) { @@ -304,9 +309,7 @@ private void failDecommissionedNodes(ClusterState state) { ActionListener statusUpdateListener = new ActionListener<>() { @Override public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { - logger.info( - "successfully updated decommission status" - ); + logger.info("successfully updated decommission status"); } @Override @@ -359,42 +362,31 @@ private void updateMetadataWithDecommissionStatus( DecommissionStatus decommissionStatus, ActionListener listener ) { - clusterService.submitStateUpdateTask( - decommissionStatus.status(), - new ClusterStateUpdateTask(Priority.URGENT) { - @Override - public ClusterState execute(ClusterState currentState) throws Exception { - Metadata metadata = currentState.metadata(); - DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.custom(DecommissionAttributeMetadata.TYPE); - assert decommissionAttributeMetadata != null - && decommissionAttributeMetadata.decommissionAttribute() != null - : "failed to update status for decommission. metadata doesn't exist or invalid"; - assert assertIncrementalStatusOrFailed(decommissionAttributeMetadata.status(), decommissionStatus); - Metadata.Builder mdBuilder = Metadata.builder(metadata); - DecommissionAttributeMetadata newMetadata = decommissionAttributeMetadata.withUpdatedStatus(decommissionStatus); - mdBuilder.putCustom(DecommissionAttributeMetadata.TYPE, newMetadata); - return ClusterState.builder(currentState).metadata(mdBuilder).build(); - } - - @Override - public void onFailure(String source, Exception e) { - logger.error( - () -> new ParameterizedMessage( - "failed to mark status as [{}]", - decommissionStatus.status() - ), - e - ); - } - - @Override - public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { - listener.onResponse(new ClusterStateUpdateResponse(true)); - } + clusterService.submitStateUpdateTask(decommissionStatus.status(), new ClusterStateUpdateTask(Priority.URGENT) { + @Override + public ClusterState execute(ClusterState currentState) throws Exception { + Metadata metadata = currentState.metadata(); + DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.custom(DecommissionAttributeMetadata.TYPE); + assert decommissionAttributeMetadata != null && decommissionAttributeMetadata.decommissionAttribute() != null + : "failed to update status for decommission. metadata doesn't exist or invalid"; + assert assertIncrementalStatusOrFailed(decommissionAttributeMetadata.status(), decommissionStatus); + Metadata.Builder mdBuilder = Metadata.builder(metadata); + DecommissionAttributeMetadata newMetadata = decommissionAttributeMetadata.withUpdatedStatus(decommissionStatus); + mdBuilder.putCustom(DecommissionAttributeMetadata.TYPE, newMetadata); + return ClusterState.builder(currentState).metadata(mdBuilder).build(); + } + @Override + public void onFailure(String source, Exception e) { + logger.error(() -> new ParameterizedMessage("failed to mark status as [{}]", decommissionStatus.status()), e); + } + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + listener.onResponse(new ClusterStateUpdateResponse(true)); } - ); + + }); } private List nodesWithDecommissionAttribute(ClusterState clusterState, DecommissionAttribute decommissionAttribute) { @@ -410,7 +402,7 @@ private List nodesWithDecommissionAttribute(ClusterState clusterS nodesWithDecommissionAttribute.add(node); } } - return nodesWithDecommissionAttribute; + return nodesWithDecommissionAttribute; } private static boolean assertIncrementalStatusOrFailed(DecommissionStatus oldStatus, DecommissionStatus newStatus) { From 3eeb16a42273c21fba07c909d174c3f5bb1e1cd2 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Wed, 24 Aug 2022 16:14:49 +0530 Subject: [PATCH 132/187] Update the join validator for decommissioned attribute Signed-off-by: Rishab Nahata --- .../org/opensearch/cluster/coordination/JoinTaskExecutor.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java index 9825bebb63dd6..2eba89c72ae2b 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java @@ -40,6 +40,7 @@ import org.opensearch.cluster.NotClusterManagerException; import org.opensearch.cluster.block.ClusterBlocks; import org.opensearch.cluster.decommission.DecommissionAttribute; +import org.opensearch.cluster.decommission.DecommissionStatus; import org.opensearch.cluster.decommission.NodeDecommissionedException; import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; import org.opensearch.cluster.metadata.IndexMetadata; @@ -479,7 +480,8 @@ public static void ensureNodeNotDecommissioned(DiscoveryNode node, Metadata meta DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.custom(DecommissionAttributeMetadata.TYPE); if (decommissionAttributeMetadata != null) { DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute(); - if (decommissionAttribute != null) { + if (decommissionAttribute != null && decommissionAttributeMetadata.status() != null) { + if(decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_FAILED)) return; if (node.getAttributes().get(decommissionAttribute.attributeName()).equals(decommissionAttribute.attributeValue())) { throw new NodeDecommissionedException( "node [{}] has decommissioned attribute [{}].", From 06ea6bcb8617470bb67ea2da966ebe394e377b89 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Wed, 24 Aug 2022 20:22:38 +0530 Subject: [PATCH 133/187] Add UTs for metadata Signed-off-by: Rishab Nahata --- .../org/opensearch/OpenSearchException.java | 1 + .../DecommissionAttributeMetadata.java | 3 +- .../ExceptionSerializationTests.java | 4 + ...onAttributeMetadataSerializationTests.java | 85 +++++++++++++++++++ .../DecommissionAttributeMetadataTests.java | 51 +++++++++++ ...missionAttributeMetadataXContentTests.java | 37 ++++++++ 6 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataSerializationTests.java create mode 100644 server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataTests.java create mode 100644 server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataXContentTests.java diff --git a/server/src/main/java/org/opensearch/OpenSearchException.java b/server/src/main/java/org/opensearch/OpenSearchException.java index b1e9a27b98416..5ae83c9df70d3 100644 --- a/server/src/main/java/org/opensearch/OpenSearchException.java +++ b/server/src/main/java/org/opensearch/OpenSearchException.java @@ -69,6 +69,7 @@ import static java.util.Collections.unmodifiableMap; import static org.opensearch.Version.V_2_1_0; import static org.opensearch.Version.V_2_3_0; +import static org.opensearch.Version.V_3_0_0; import static org.opensearch.cluster.metadata.IndexMetadata.INDEX_UUID_NA_VALUE; import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken; import static org.opensearch.common.xcontent.XContentParserUtils.ensureFieldName; diff --git a/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java b/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java index c6252e9981810..2034ab34e25c3 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java @@ -137,7 +137,7 @@ public static DecommissionAttributeMetadata fromXContent(XContentParser parser) XContentParser.Token token; DecommissionAttribute decommissionAttribute = null; DecommissionStatus status = null; - if ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) { String currentFieldName = parser.currentName(); if (attributeType.equals(currentFieldName)) { @@ -162,6 +162,7 @@ public static DecommissionAttributeMetadata fromXContent(XContentParser parser) ); } decommissionAttribute = new DecommissionAttribute(fieldName, value); + token = parser.nextToken(); } else { throw new OpenSearchParseException("failed to parse attribute type [{}], unexpected type", attributeType); } diff --git a/server/src/test/java/org/opensearch/ExceptionSerializationTests.java b/server/src/test/java/org/opensearch/ExceptionSerializationTests.java index 26b0ce7e9e20c..6516cc80c7929 100644 --- a/server/src/test/java/org/opensearch/ExceptionSerializationTests.java +++ b/server/src/test/java/org/opensearch/ExceptionSerializationTests.java @@ -49,6 +49,8 @@ import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.coordination.CoordinationStateRejectedException; import org.opensearch.cluster.coordination.NoClusterManagerBlockService; +import org.opensearch.cluster.decommission.DecommissionFailedException; +import org.opensearch.cluster.decommission.NodeDecommissionedException; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.routing.IllegalShardRoutingStateException; import org.opensearch.cluster.routing.ShardRouting; @@ -860,6 +862,8 @@ public void testIds() { ids.put(160, NoSeedNodeLeftException.class); ids.put(161, ReplicationFailedException.class); ids.put(162, PrimaryShardClosedException.class); + ids.put(163, DecommissionFailedException.class); + ids.put(164, NodeDecommissionedException.class); Map, Integer> reverse = new HashMap<>(); for (Map.Entry> entry : ids.entrySet()) { diff --git a/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataSerializationTests.java b/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataSerializationTests.java new file mode 100644 index 0000000000000..d81c05b8e8da0 --- /dev/null +++ b/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataSerializationTests.java @@ -0,0 +1,85 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.metadata; + +import org.opensearch.cluster.ClusterModule; +import org.opensearch.cluster.Diff; +import org.opensearch.cluster.decommission.DecommissionAttribute; +import org.opensearch.cluster.decommission.DecommissionStatus; +import org.opensearch.common.io.stream.NamedWriteableRegistry; +import org.opensearch.common.io.stream.Writeable; +import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.test.AbstractDiffableSerializationTestCase; + +import java.io.IOException; + +public class DecommissionAttributeMetadataSerializationTests extends AbstractDiffableSerializationTestCase { + + @Override + protected Writeable.Reader instanceReader() { + return DecommissionAttributeMetadata::new; + } + + @Override + protected Metadata.Custom createTestInstance() { + String attributeName = randomAlphaOfLength(6); + String attributeValue = randomAlphaOfLength(6); + DecommissionAttribute decommissionAttribute = new DecommissionAttribute(attributeName, attributeValue); + DecommissionStatus decommissionStatus = randomFrom(DecommissionStatus.values()); + return new DecommissionAttributeMetadata(decommissionAttribute, decommissionStatus); + } + + @Override + protected Metadata.Custom mutateInstance(Metadata.Custom instance) { + return randomValueOtherThan(instance, this::createTestInstance); + } + + @Override + protected Metadata.Custom makeTestChanges(Metadata.Custom testInstance) { + DecommissionAttributeMetadata decommissionAttributeMetadata = (DecommissionAttributeMetadata) testInstance; + DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute(); + String attributeName = decommissionAttribute.attributeName(); + String attributeValue = decommissionAttribute.attributeValue(); + DecommissionStatus decommissionStatus = decommissionAttributeMetadata.status(); + if (randomBoolean()) { + decommissionStatus = randomFrom(DecommissionStatus.values()); + } + if (randomBoolean()) { + attributeName = randomAlphaOfLength(6); + } + if(randomBoolean()) { + attributeValue = randomAlphaOfLength(6); + } + return new DecommissionAttributeMetadata( + new DecommissionAttribute(attributeName, attributeValue), + decommissionStatus + ); + } + + @Override + protected Writeable.Reader> diffReader() { + return DecommissionAttributeMetadata::readDiffFrom; + } + + @Override + protected NamedWriteableRegistry getNamedWriteableRegistry() { + return new NamedWriteableRegistry(ClusterModule.getNamedWriteables()); + } + + @Override + protected Metadata.Custom doParseInstance(XContentParser parser) throws IOException { + assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken()); + DecommissionAttributeMetadata decommissionAttributeMetadata = DecommissionAttributeMetadata.fromXContent(parser); + assertEquals(XContentParser.Token.END_OBJECT, parser.currentToken()); + return new DecommissionAttributeMetadata( + decommissionAttributeMetadata.decommissionAttribute(), + decommissionAttributeMetadata.status() + ); + } +} diff --git a/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataTests.java b/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataTests.java new file mode 100644 index 0000000000000..bff57daef6109 --- /dev/null +++ b/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataTests.java @@ -0,0 +1,51 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.metadata; + +import org.opensearch.cluster.decommission.DecommissionAttribute; +import org.opensearch.cluster.decommission.DecommissionStatus; +import org.opensearch.common.io.stream.NamedWriteableRegistry; +import org.opensearch.test.AbstractNamedWriteableTestCase; + +import java.io.IOException; +import java.util.Collections; + +public class DecommissionAttributeMetadataTests extends AbstractNamedWriteableTestCase { + @Override + protected DecommissionAttributeMetadata createTestInstance() { + String attributeName = randomAlphaOfLength(6); + String attributeValue = randomAlphaOfLength(6); + DecommissionAttribute decommissionAttribute = new DecommissionAttribute(attributeName, attributeValue); + DecommissionStatus decommissionStatus = randomFrom(DecommissionStatus.values()); + return new DecommissionAttributeMetadata(decommissionAttribute, decommissionStatus); + } + + @Override + protected DecommissionAttributeMetadata mutateInstance(DecommissionAttributeMetadata instance) throws IOException { + return randomValueOtherThan(instance, this::createTestInstance); + } + + @Override + protected NamedWriteableRegistry getNamedWriteableRegistry() { + return new NamedWriteableRegistry( + Collections.singletonList( + new NamedWriteableRegistry.Entry( + DecommissionAttributeMetadata.class, + DecommissionAttributeMetadata.TYPE, + DecommissionAttributeMetadata::new + ) + ) + ); + } + + @Override + protected Class categoryClass() { + return DecommissionAttributeMetadata.class; + } +} diff --git a/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataXContentTests.java b/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataXContentTests.java new file mode 100644 index 0000000000000..c632839acd4ca --- /dev/null +++ b/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataXContentTests.java @@ -0,0 +1,37 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.metadata; + +import org.opensearch.cluster.decommission.DecommissionAttribute; +import org.opensearch.cluster.decommission.DecommissionStatus; +import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.test.AbstractXContentTestCase; + +import java.io.IOException; + +public class DecommissionAttributeMetadataXContentTests extends AbstractXContentTestCase { + @Override + protected DecommissionAttributeMetadata createTestInstance() { + String attributeName = randomAlphaOfLength(6); + String attributeValue = randomAlphaOfLength(6); + DecommissionAttribute decommissionAttribute = new DecommissionAttribute(attributeName, attributeValue); + DecommissionStatus decommissionStatus = randomFrom(DecommissionStatus.values()); + return new DecommissionAttributeMetadata(decommissionAttribute, decommissionStatus); + } + + @Override + protected DecommissionAttributeMetadata doParseInstance(XContentParser parser) throws IOException { + return DecommissionAttributeMetadata.fromXContent(parser); + } + + @Override + protected boolean supportsUnknownFields() { + return false; + } +} From 86583892909cbcd77f6c65918eceda7e71275634 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Thu, 25 Aug 2022 00:45:09 +0530 Subject: [PATCH 134/187] Add UTs for JoinTaskExecutor changes Signed-off-by: Rishab Nahata --- .../coordination/JoinTaskExecutor.java | 13 ++- .../coordination/JoinTaskExecutorTests.java | 86 +++++++++++++++++++ 2 files changed, 96 insertions(+), 3 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java index 2eba89c72ae2b..469c0b786056f 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java @@ -480,9 +480,16 @@ public static void ensureNodeNotDecommissioned(DiscoveryNode node, Metadata meta DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.custom(DecommissionAttributeMetadata.TYPE); if (decommissionAttributeMetadata != null) { DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute(); - if (decommissionAttribute != null && decommissionAttributeMetadata.status() != null) { - if(decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_FAILED)) return; - if (node.getAttributes().get(decommissionAttribute.attributeName()).equals(decommissionAttribute.attributeValue())) { + DecommissionStatus status = decommissionAttributeMetadata.status(); + if (decommissionAttribute != null && status != null) { + // We will let the node join the cluster if the current status is INIT or FAILED + if(status.equals(DecommissionStatus.DECOMMISSION_FAILED) || status.equals(DecommissionStatus.DECOMMISSION_INIT)) return; + if (node.getAttributes().get(decommissionAttribute.attributeName()).equals(decommissionAttribute.attributeValue()) + && ( + status.equals(DecommissionStatus.DECOMMISSION_IN_PROGRESS) + || status.equals(DecommissionStatus.DECOMMISSION_SUCCESSFUL + ) + )) { throw new NodeDecommissionedException( "node [{}] has decommissioned attribute [{}].", node.toString(), diff --git a/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java b/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java index 02e502e762561..a3c3b9a9b9c7b 100644 --- a/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java +++ b/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java @@ -36,9 +36,15 @@ import org.opensearch.cluster.ClusterName; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateTaskExecutor; +import org.opensearch.cluster.decommission.DecommissionAttribute; +import org.opensearch.cluster.decommission.DecommissionFailedException; +import org.opensearch.cluster.decommission.DecommissionStatus; +import org.opensearch.cluster.decommission.NodeDecommissionedException; +import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.node.DiscoveryNodeRole; import org.opensearch.cluster.node.DiscoveryNodes; import org.opensearch.cluster.routing.RerouteService; import org.opensearch.cluster.routing.allocation.AllocationService; @@ -48,8 +54,11 @@ import org.opensearch.test.OpenSearchTestCase; import org.opensearch.test.VersionUtils; +import java.util.Collections; import java.util.HashSet; +import java.util.Map; +import static java.util.Collections.emptyMap; import static org.hamcrest.Matchers.is; import static org.opensearch.test.VersionUtils.allVersions; import static org.opensearch.test.VersionUtils.maxCompatibleVersion; @@ -216,4 +225,81 @@ public void testIsBecomeClusterManagerTask() { JoinTaskExecutor.Task joinTaskOfClusterManager = JoinTaskExecutor.newBecomeClusterManagerTask(); assertThat(joinTaskOfClusterManager.isBecomeClusterManagerTask(), is(true)); } + + public void testJoinClusterWithNoDecommission() { + Settings.builder().build(); + Metadata.Builder metaBuilder = Metadata.builder(); + Metadata metadata = metaBuilder.build(); + DiscoveryNode discoveryNode = newDiscoveryNode(Collections.singletonMap("zone", "zone-2")); + JoinTaskExecutor.ensureNodeNotDecommissioned(discoveryNode, metadata); + } + + public void testPreventJoinClusterWithDecommission() { + Settings.builder().build(); + DecommissionAttribute decommissionAttribute = new DecommissionAttribute("zone", "zone-1"); + DecommissionStatus decommissionStatus = randomFrom( + DecommissionStatus.DECOMMISSION_IN_PROGRESS, + DecommissionStatus.DECOMMISSION_SUCCESSFUL + ); + DecommissionAttributeMetadata decommissionAttributeMetadata = new DecommissionAttributeMetadata( + decommissionAttribute, + decommissionStatus + ); + Metadata.Builder metaBuilder = Metadata.builder(); + metaBuilder.putCustom(DecommissionAttributeMetadata.TYPE, decommissionAttributeMetadata); + Metadata metadata = metaBuilder.build(); + + DiscoveryNode discoveryNode = newDiscoveryNode(Collections.singletonMap("zone", "zone-1")); + expectThrows( + NodeDecommissionedException.class, + () -> JoinTaskExecutor.ensureNodeNotDecommissioned(discoveryNode, metadata) + ); + } + + public void testJoinClusterWithDifferentDecommission() { + Settings.builder().build(); + DecommissionAttribute decommissionAttribute = new DecommissionAttribute("zone", "zone-1"); + DecommissionStatus decommissionStatus = randomFrom(DecommissionStatus.values()); + DecommissionAttributeMetadata decommissionAttributeMetadata = new DecommissionAttributeMetadata( + decommissionAttribute, + decommissionStatus + ); + Metadata.Builder metaBuilder = Metadata.builder(); + metaBuilder.putCustom(DecommissionAttributeMetadata.TYPE, decommissionAttributeMetadata); + Metadata metadata = metaBuilder.build(); + + DiscoveryNode discoveryNode = newDiscoveryNode(Collections.singletonMap("zone", "zone-2")); + JoinTaskExecutor.ensureNodeNotDecommissioned(discoveryNode, metadata); + } + + public void testJoinClusterWithDecommissionFailedOrInitOrRecommission() { + Settings.builder().build(); + DecommissionAttribute decommissionAttribute = new DecommissionAttribute("zone", "zone-1"); + DecommissionStatus decommissionStatus = randomFrom( + DecommissionStatus.DECOMMISSION_INIT, + DecommissionStatus.DECOMMISSION_FAILED, + DecommissionStatus.RECOMMISSION_IN_PROGRESS + ); + DecommissionAttributeMetadata decommissionAttributeMetadata = new DecommissionAttributeMetadata( + decommissionAttribute, + decommissionStatus + ); + Metadata.Builder metaBuilder = Metadata.builder(); + metaBuilder.putCustom(DecommissionAttributeMetadata.TYPE, decommissionAttributeMetadata); + Metadata metadata = metaBuilder.build(); + + DiscoveryNode discoveryNode = newDiscoveryNode(Collections.singletonMap("zone", "zone-1")); + JoinTaskExecutor.ensureNodeNotDecommissioned(discoveryNode, metadata); + } + + private DiscoveryNode newDiscoveryNode(Map attributes) { + return new DiscoveryNode( + randomAlphaOfLength(10), + randomAlphaOfLength(10), + buildNewFakeTransportAddress(), + attributes, + Collections.singleton(DiscoveryNodeRole.CLUSTER_MANAGER_ROLE), + Version.CURRENT + ); + } } From 2dbf28459bfc3d56bbd8a449355c84afaccec541 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Thu, 25 Aug 2022 00:47:20 +0530 Subject: [PATCH 135/187] Fix Signed-off-by: Rishab Nahata --- .../org/opensearch/cluster/coordination/JoinTaskExecutor.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java index 469c0b786056f..7008474222aef 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java @@ -482,8 +482,7 @@ public static void ensureNodeNotDecommissioned(DiscoveryNode node, Metadata meta DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute(); DecommissionStatus status = decommissionAttributeMetadata.status(); if (decommissionAttribute != null && status != null) { - // We will let the node join the cluster if the current status is INIT or FAILED - if(status.equals(DecommissionStatus.DECOMMISSION_FAILED) || status.equals(DecommissionStatus.DECOMMISSION_INIT)) return; + // We will let the node join the cluster if the current status is not IN_PROGRESS or SUCCESSFUL if (node.getAttributes().get(decommissionAttribute.attributeName()).equals(decommissionAttribute.attributeValue()) && ( status.equals(DecommissionStatus.DECOMMISSION_IN_PROGRESS) From 658800e87c16bb274fc35bbabb0da12ed16f3439 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Thu, 25 Aug 2022 16:17:26 +0530 Subject: [PATCH 136/187] Test files Signed-off-by: Rishab Nahata --- .../decommission/DecommissionHelperTests.java | 14 ++++++++++++++ .../decommission/DecommissionServiceTests.java | 14 ++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 server/src/test/java/org/opensearch/cluster/decommission/DecommissionHelperTests.java create mode 100644 server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionHelperTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionHelperTests.java new file mode 100644 index 0000000000000..289d5147512e1 --- /dev/null +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionHelperTests.java @@ -0,0 +1,14 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.decommission; + +import org.opensearch.test.OpenSearchTestCase; + +public class DecommissionHelperTests extends OpenSearchTestCase { +} diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java new file mode 100644 index 0000000000000..ea4aee984df98 --- /dev/null +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java @@ -0,0 +1,14 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.decommission; + +import org.opensearch.test.OpenSearchTestCase; + +public class DecommissionServiceTests extends OpenSearchTestCase { +} From de3290df2ae8cbdfd5944f5a0cc18f28d84c4ddc Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Thu, 25 Aug 2022 17:14:49 +0530 Subject: [PATCH 137/187] Move observer logic to helper Signed-off-by: Rishab Nahata --- .../decommission/DecommissionHelper.java | 52 +++++++++++++-- .../decommission/DecommissionService.java | 58 ++++++++--------- .../decommission/DecommissionHelperTests.java | 65 +++++++++++++++++++ 3 files changed, 139 insertions(+), 36 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java index ce7befacaef98..7b2ae71288d71 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java @@ -10,17 +10,24 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.opensearch.action.ActionListener; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.ClusterStateObserver; import org.opensearch.cluster.ClusterStateTaskConfig; import org.opensearch.cluster.ClusterStateTaskListener; +import org.opensearch.cluster.ack.ClusterStateUpdateResponse; import org.opensearch.cluster.coordination.NodeRemovalClusterStateTaskExecutor; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.routing.allocation.AllocationService; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Priority; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.threadpool.ThreadPool; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.function.Predicate; /** * Helper executor class to remove list of nodes from the cluster @@ -34,24 +41,61 @@ public class DecommissionHelper { private final NodeRemovalClusterStateTaskExecutor nodeRemovalExecutor; private final ClusterService clusterService; + private final ThreadPool threadPool; - DecommissionHelper(ClusterService clusterService, AllocationService allocationService) { + DecommissionHelper( + ClusterService clusterService, + AllocationService allocationService, + ThreadPool threadPool + ) { this.clusterService = clusterService; this.nodeRemovalExecutor = new NodeRemovalClusterStateTaskExecutor(allocationService, logger); + this.threadPool = threadPool; } - public void handleNodesDecommissionRequest(List nodesToBeDecommissioned, String reason) { + public void handleNodesDecommissionRequest( + List nodesToBeDecommissioned, + String reason, + TimeValue timeout, + Predicate allDecommissionedNodesRemoved, + ActionListener nodesRemovedListener + ) { final Map nodesDecommissionTasks = new LinkedHashMap<>(); nodesToBeDecommissioned.forEach(discoveryNode -> { final NodeRemovalClusterStateTaskExecutor.Task task = new NodeRemovalClusterStateTaskExecutor.Task(discoveryNode, reason); nodesDecommissionTasks.put(task, nodeRemovalExecutor); }); - final String source = "node-decommissioned"; clusterService.submitStateUpdateTasks( - source, + "node-decommissioned", nodesDecommissionTasks, ClusterStateTaskConfig.build(Priority.IMMEDIATE), nodeRemovalExecutor ); + + final ClusterStateObserver observer = new ClusterStateObserver( + clusterService, + timeout, + logger, + threadPool.getThreadContext() + ); + + observer.waitForNextChange(new ClusterStateObserver.Listener() { + @Override + public void onNewClusterState(ClusterState state) { + logger.info("successfully removed all decommissioned nodes [{}] from the cluster", nodesToBeDecommissioned.toString()); + nodesRemovedListener.onResponse(new ClusterStateUpdateResponse(true)); + } + + @Override + public void onClusterServiceClose() { + logger.debug("cluster service closed while waiting for removal of decommissioned nodes."); + } + + @Override + public void onTimeout(TimeValue timeout) { + logger.info("timed out while waiting for removal of decommissioned nodes"); + nodesRemovedListener.onResponse(new ClusterStateUpdateResponse(false)); + } + }, allDecommissionedNodesRemoved); } } diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 139b241ea00a4..c1e0045000ee7 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -84,7 +84,11 @@ public DecommissionService( this.clusterService = clusterService; this.transportService = transportService; this.threadPool = threadPool; - this.decommissionHelper = new DecommissionHelper(clusterService, allocationService); + this.decommissionHelper = new DecommissionHelper( + clusterService, + allocationService, + threadPool + ); this.awarenessAttributes = AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING.get(settings); clusterSettings.addSettingsUpdateConsumer( AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING, @@ -318,44 +322,34 @@ public void onFailure(Exception e) { } }; - // execute decommissioning - decommissionHelper.handleNodesDecommissionRequest( - nodesWithDecommissionAttribute(state, decommissionAttribute), - "nodes-decommissioned" - ); + ActionListener nodesRemovalListener = new ActionListener<>() { + @Override + public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { + DecommissionStatus updateStatusTo = clusterStateUpdateResponse.isAcknowledged() ? + DecommissionStatus.DECOMMISSION_SUCCESSFUL : DecommissionStatus.DECOMMISSION_FAILED; + updateMetadataWithDecommissionStatus(updateStatusTo, statusUpdateListener); + } - final ClusterStateObserver observer = new ClusterStateObserver( - clusterService, - TimeValue.timeValueSeconds(30L), // should this be a setting? - logger, - threadPool.getThreadContext() - ); + @Override + public void onFailure(Exception e) { + logger.error("failed to update the decommission status"); + } + }; final Predicate allDecommissionedNodesRemoved = clusterState -> { List nodesWithDecommissionAttribute = nodesWithDecommissionAttribute(clusterState, decommissionAttribute); return nodesWithDecommissionAttribute.size() == 0; }; - observer.waitForNextChange(new ClusterStateObserver.Listener() { - @Override - public void onNewClusterState(ClusterState state) { - logger.info("successfully removed all decommissioned nodes from the cluster"); - clearVotingConfigAfterSuccessfulDecommission(); - updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_SUCCESSFUL, statusUpdateListener); - } - - @Override - public void onClusterServiceClose() { - logger.debug("cluster service closed while waiting for removal of decommissioned nodes."); - } - - @Override - public void onTimeout(TimeValue timeout) { - logger.info("timed out while waiting for removal of decommissioned nodes"); - clearVotingConfigAfterSuccessfulDecommission(); - updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_FAILED, statusUpdateListener); - } - }, allDecommissionedNodesRemoved); + // execute decommissioning + decommissionHelper.handleNodesDecommissionRequest( + nodesWithDecommissionAttribute(state, decommissionAttribute), + "nodes-decommissioned", + TimeValue.timeValueSeconds(30L), + allDecommissionedNodesRemoved, + nodesRemovalListener + ); + clearVotingConfigAfterSuccessfulDecommission(); } private void updateMetadataWithDecommissionStatus( diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionHelperTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionHelperTests.java index 289d5147512e1..722fad288e6bb 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionHelperTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionHelperTests.java @@ -8,7 +8,72 @@ package org.opensearch.cluster.decommission; +import org.opensearch.Version; +import org.opensearch.cluster.ClusterName; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.node.DiscoveryNodeRole; +import org.opensearch.cluster.node.DiscoveryNodes; +import org.opensearch.cluster.routing.allocation.AllocationService; +import org.opensearch.cluster.service.ClusterService; import org.opensearch.test.OpenSearchTestCase; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static java.util.Collections.singletonMap; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + public class DecommissionHelperTests extends OpenSearchTestCase { + +// public void testRemoveNodesForDecommissionRequest() { +// final AllocationService allocationService = mock(AllocationService.class); +// final ClusterService clusterService = mock(ClusterService.class); +// +// ClusterState clusterState = ClusterState.builder(new ClusterName("test")).build(); +// +// logger.info("--> adding five nodes on same zone_1"); +// clusterState = addNodes(clusterState, "zone_1", "node1", "node2", "node3", "node4", "node5"); +// +// logger.info("--> adding five nodes on same zone_2"); +// clusterState = addNodes(clusterState, "zone_2", "node6", "node7", "node8", "node9", "node10"); +// +// logger.info("--> adding five nodes on same zone_3"); +// clusterState = addNodes(clusterState, "zone_3", "node11", "node12", "node13", "node14", "node15"); +// +// when(clusterService.state()).thenReturn(clusterState); +// +// final DecommissionHelper decommissionHelper = new DecommissionHelper(clusterService, allocationService); +// +// List nodesToBeRemoved = new ArrayList<>(); +// nodesToBeRemoved.add(clusterState.nodes().get("node11")); +// nodesToBeRemoved.add(clusterState.nodes().get("node12")); +// nodesToBeRemoved.add(clusterState.nodes().get("node13")); +// nodesToBeRemoved.add(clusterState.nodes().get("node14")); +// nodesToBeRemoved.add(clusterState.nodes().get("node15")); +// +// decommissionHelper.handleNodesDecommissionRequest(nodesToBeRemoved, "unit-test"); +// assertEquals((clusterService.state().nodes().getSize()), 10); +// } + + private ClusterState addNodes(ClusterState clusterState, String zone, String... nodeIds) { + DiscoveryNodes.Builder nodeBuilder = DiscoveryNodes.builder(clusterState.nodes()); + org.opensearch.common.collect.List.of(nodeIds).forEach(nodeId -> nodeBuilder.add(newNode(nodeId, singletonMap("zone", zone)))); + clusterState = ClusterState.builder(clusterState).nodes(nodeBuilder).build(); + return clusterState; + } + + private DiscoveryNode newNode(String nodeId, Map attributes) { + return new DiscoveryNode(nodeId, buildNewFakeTransportAddress(), attributes, CLUSTER_MANAGER_DATA_ROLES, Version.CURRENT); + } + + final private static Set CLUSTER_MANAGER_DATA_ROLES = Collections.unmodifiableSet( + new HashSet<>(Arrays.asList(DiscoveryNodeRole.CLUSTER_MANAGER_ROLE, DiscoveryNodeRole.DATA_ROLE)) + ); } From d5d323d08dd31c305e1cab37b8201b97b80e011f Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Thu, 25 Aug 2022 17:23:56 +0530 Subject: [PATCH 138/187] fix msg Signed-off-by: Rishab Nahata --- .../opensearch/cluster/decommission/DecommissionService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index c1e0045000ee7..b034daa567361 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -332,7 +332,7 @@ public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { @Override public void onFailure(Exception e) { - logger.error("failed to update the decommission status"); + logger.error("error while waiting for decommissioned nodes to be removed", e); } }; @@ -341,7 +341,7 @@ public void onFailure(Exception e) { return nodesWithDecommissionAttribute.size() == 0; }; - // execute decommissioning + // execute nodes decommissioning and wait for it to complete decommissionHelper.handleNodesDecommissionRequest( nodesWithDecommissionAttribute(state, decommissionAttribute), "nodes-decommissioned", From 564107e81f74ad3be8d8648d0f70d068b43fb637 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Fri, 26 Aug 2022 14:25:47 +0530 Subject: [PATCH 139/187] Move predicate to helper Signed-off-by: Rishab Nahata --- .../decommission/DecommissionHelper.java | 20 ++++++++++--- .../decommission/DecommissionService.java | 12 +++----- .../decommission/DecommissionHelperTests.java | 29 ++++++++++++++++++- 3 files changed, 48 insertions(+), 13 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java index 7b2ae71288d71..935fe42873bc9 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java @@ -24,9 +24,10 @@ import org.opensearch.common.unit.TimeValue; import org.opensearch.threadpool.ThreadPool; +import java.util.Iterator; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; +import java.util.Set; import java.util.function.Predicate; /** @@ -54,10 +55,9 @@ public class DecommissionHelper { } public void handleNodesDecommissionRequest( - List nodesToBeDecommissioned, + Set nodesToBeDecommissioned, String reason, TimeValue timeout, - Predicate allDecommissionedNodesRemoved, ActionListener nodesRemovedListener ) { final Map nodesDecommissionTasks = new LinkedHashMap<>(); @@ -72,6 +72,18 @@ public void handleNodesDecommissionRequest( nodeRemovalExecutor ); + Predicate allDecommissionedNodesRemovedPredicate = clusterState -> { + Iterator nodesIter = clusterState.nodes().getNodes().valuesIt(); + while (nodesIter.hasNext()) { + final DiscoveryNode node = nodesIter.next(); + // check if the node is part of node decommissioned list + if (nodesToBeDecommissioned.contains(node)) { + return false; + } + } + return true; + }; + final ClusterStateObserver observer = new ClusterStateObserver( clusterService, timeout, @@ -96,6 +108,6 @@ public void onTimeout(TimeValue timeout) { logger.info("timed out while waiting for removal of decommissioned nodes"); nodesRemovedListener.onResponse(new ClusterStateUpdateResponse(false)); } - }, allDecommissionedNodesRemoved); + }, allDecommissionedNodesRemovedPredicate); } } diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index b034daa567361..b0e2e2f1f7a46 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -42,8 +42,10 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Set; import java.util.function.Predicate; /** @@ -336,17 +338,11 @@ public void onFailure(Exception e) { } }; - final Predicate allDecommissionedNodesRemoved = clusterState -> { - List nodesWithDecommissionAttribute = nodesWithDecommissionAttribute(clusterState, decommissionAttribute); - return nodesWithDecommissionAttribute.size() == 0; - }; - // execute nodes decommissioning and wait for it to complete decommissionHelper.handleNodesDecommissionRequest( nodesWithDecommissionAttribute(state, decommissionAttribute), "nodes-decommissioned", TimeValue.timeValueSeconds(30L), - allDecommissionedNodesRemoved, nodesRemovalListener ); clearVotingConfigAfterSuccessfulDecommission(); @@ -383,8 +379,8 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS }); } - private List nodesWithDecommissionAttribute(ClusterState clusterState, DecommissionAttribute decommissionAttribute) { - List nodesWithDecommissionAttribute = new ArrayList<>(); + private Set nodesWithDecommissionAttribute(ClusterState clusterState, DecommissionAttribute decommissionAttribute) { + Set nodesWithDecommissionAttribute = new HashSet<>(); final Predicate shouldRemoveNodePredicate = discoveryNode -> nodeHasDecommissionedAttribute( discoveryNode, decommissionAttribute diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionHelperTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionHelperTests.java index 722fad288e6bb..9452c0e94c262 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionHelperTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionHelperTests.java @@ -8,15 +8,24 @@ package org.opensearch.cluster.decommission; +import org.junit.AfterClass; +import org.junit.BeforeClass; import org.opensearch.Version; import org.opensearch.cluster.ClusterName; import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.ClusterStateObserver; +import org.opensearch.cluster.coordination.CoordinationMetadata; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.node.DiscoveryNodeRole; import org.opensearch.cluster.node.DiscoveryNodes; import org.opensearch.cluster.routing.allocation.AllocationService; import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.settings.ClusterSettings; +import org.opensearch.common.settings.Settings; import org.opensearch.test.OpenSearchTestCase; +import org.opensearch.threadpool.TestThreadPool; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportService; import java.util.ArrayList; import java.util.Arrays; @@ -26,12 +35,30 @@ import java.util.Map; import java.util.Set; +import static java.util.Collections.emptyMap; +import static java.util.Collections.emptySet; import static java.util.Collections.singletonMap; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static org.opensearch.test.ClusterServiceUtils.createClusterService; public class DecommissionHelperTests extends OpenSearchTestCase { + private static ThreadPool threadPool; + private static ClusterService clusterService; + + @BeforeClass + public static void createThreadPoolAndClusterService() { + threadPool = new TestThreadPool("test", Settings.EMPTY); + clusterService = createClusterService(threadPool); + } + + @AfterClass + public static void shutdownThreadPoolAndClusterService() { + clusterService.stop(); + threadPool.shutdown(); + } + // public void testRemoveNodesForDecommissionRequest() { // final AllocationService allocationService = mock(AllocationService.class); // final ClusterService clusterService = mock(ClusterService.class); @@ -69,7 +96,7 @@ private ClusterState addNodes(ClusterState clusterState, String zone, String... return clusterState; } - private DiscoveryNode newNode(String nodeId, Map attributes) { + private static DiscoveryNode newNode(String nodeId, Map attributes) { return new DiscoveryNode(nodeId, buildNewFakeTransportAddress(), attributes, CLUSTER_MANAGER_DATA_ROLES, Version.CURRENT); } From c3be33f3b232328ff11de18934ab958325c62355 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Mon, 29 Aug 2022 14:45:40 +0530 Subject: [PATCH 140/187] test Signed-off-by: Rishab Nahata --- ...elper.java => DecommissionController.java} | 51 ++++- .../decommission/DecommissionService.java | 50 +---- .../DecommissionControllerTests.java | 179 ++++++++++++++++++ .../decommission/DecommissionHelperTests.java | 106 ----------- 4 files changed, 232 insertions(+), 154 deletions(-) rename server/src/main/java/org/opensearch/cluster/decommission/{DecommissionHelper.java => DecommissionController.java} (61%) create mode 100644 server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java delete mode 100644 server/src/test/java/org/opensearch/cluster/decommission/DecommissionHelperTests.java diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java similarity index 61% rename from server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java rename to server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java index 935fe42873bc9..87fe15f35be38 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java @@ -10,13 +10,17 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.message.ParameterizedMessage; import org.opensearch.action.ActionListener; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateObserver; import org.opensearch.cluster.ClusterStateTaskConfig; import org.opensearch.cluster.ClusterStateTaskListener; +import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; import org.opensearch.cluster.coordination.NodeRemovalClusterStateTaskExecutor; +import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; +import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.routing.allocation.AllocationService; import org.opensearch.cluster.service.ClusterService; @@ -36,15 +40,15 @@ * @opensearch.internal */ -public class DecommissionHelper { +public class DecommissionController { - private static final Logger logger = LogManager.getLogger(DecommissionHelper.class); + private static final Logger logger = LogManager.getLogger(DecommissionController.class); private final NodeRemovalClusterStateTaskExecutor nodeRemovalExecutor; private final ClusterService clusterService; private final ThreadPool threadPool; - DecommissionHelper( + DecommissionController( ClusterService clusterService, AllocationService allocationService, ThreadPool threadPool @@ -110,4 +114,45 @@ public void onTimeout(TimeValue timeout) { } }, allDecommissionedNodesRemovedPredicate); } + + public void updateMetadataWithDecommissionStatus( + DecommissionStatus decommissionStatus, + ActionListener listener + ) { + clusterService.submitStateUpdateTask(decommissionStatus.status(), new ClusterStateUpdateTask(Priority.URGENT) { + @Override + public ClusterState execute(ClusterState currentState) throws Exception { + Metadata metadata = currentState.metadata(); + DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.custom(DecommissionAttributeMetadata.TYPE); + assert decommissionAttributeMetadata != null && decommissionAttributeMetadata.decommissionAttribute() != null + : "failed to update status for decommission. metadata doesn't exist or invalid"; + assert assertIncrementalStatusOrFailed(decommissionAttributeMetadata.status(), decommissionStatus); + Metadata.Builder mdBuilder = Metadata.builder(metadata); + DecommissionAttributeMetadata newMetadata = decommissionAttributeMetadata.withUpdatedStatus(decommissionStatus); + mdBuilder.putCustom(DecommissionAttributeMetadata.TYPE, newMetadata); + return ClusterState.builder(currentState).metadata(mdBuilder).build(); + } + + @Override + public void onFailure(String source, Exception e) { + logger.error(() -> new ParameterizedMessage("failed to mark status as [{}]", decommissionStatus.status()), e); + } + + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + listener.onResponse(new ClusterStateUpdateResponse(true)); + } + + }); + } + + private static boolean assertIncrementalStatusOrFailed(DecommissionStatus oldStatus, DecommissionStatus newStatus) { + if (oldStatus == null || newStatus.equals(DecommissionStatus.DECOMMISSION_FAILED)) return true; + else if (newStatus.equals(DecommissionStatus.DECOMMISSION_SUCCESSFUL)) { + return oldStatus.equals(DecommissionStatus.DECOMMISSION_IN_PROGRESS); + } else if (newStatus.equals(DecommissionStatus.DECOMMISSION_IN_PROGRESS)) { + return oldStatus.equals(DecommissionStatus.DECOMMISSION_INIT); + } + return true; + } } diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index b0e2e2f1f7a46..971da583eabe5 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -19,7 +19,6 @@ import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsRequest; import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsResponse; import org.opensearch.cluster.ClusterState; -import org.opensearch.cluster.ClusterStateObserver; import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.NotClusterManagerException; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; @@ -71,7 +70,7 @@ public class DecommissionService { private final ClusterService clusterService; private final TransportService transportService; private final ThreadPool threadPool; - private final DecommissionHelper decommissionHelper; + private final DecommissionController decommissionController; private volatile List awarenessAttributes; @Inject @@ -86,7 +85,7 @@ public DecommissionService( this.clusterService = clusterService; this.transportService = transportService; this.threadPool = threadPool; - this.decommissionHelper = new DecommissionHelper( + this.decommissionController = new DecommissionController( clusterService, allocationService, threadPool @@ -302,7 +301,7 @@ public void onFailure(Exception e) { ); } }; - updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_IN_PROGRESS, listener); + decommissionController.updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_IN_PROGRESS, listener); // TODO - code for graceful decommission } @@ -329,7 +328,7 @@ public void onFailure(Exception e) { public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { DecommissionStatus updateStatusTo = clusterStateUpdateResponse.isAcknowledged() ? DecommissionStatus.DECOMMISSION_SUCCESSFUL : DecommissionStatus.DECOMMISSION_FAILED; - updateMetadataWithDecommissionStatus(updateStatusTo, statusUpdateListener); + decommissionController.updateMetadataWithDecommissionStatus(updateStatusTo, statusUpdateListener); } @Override @@ -339,7 +338,7 @@ public void onFailure(Exception e) { }; // execute nodes decommissioning and wait for it to complete - decommissionHelper.handleNodesDecommissionRequest( + decommissionController.handleNodesDecommissionRequest( nodesWithDecommissionAttribute(state, decommissionAttribute), "nodes-decommissioned", TimeValue.timeValueSeconds(30L), @@ -348,36 +347,7 @@ public void onFailure(Exception e) { clearVotingConfigAfterSuccessfulDecommission(); } - private void updateMetadataWithDecommissionStatus( - DecommissionStatus decommissionStatus, - ActionListener listener - ) { - clusterService.submitStateUpdateTask(decommissionStatus.status(), new ClusterStateUpdateTask(Priority.URGENT) { - @Override - public ClusterState execute(ClusterState currentState) throws Exception { - Metadata metadata = currentState.metadata(); - DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.custom(DecommissionAttributeMetadata.TYPE); - assert decommissionAttributeMetadata != null && decommissionAttributeMetadata.decommissionAttribute() != null - : "failed to update status for decommission. metadata doesn't exist or invalid"; - assert assertIncrementalStatusOrFailed(decommissionAttributeMetadata.status(), decommissionStatus); - Metadata.Builder mdBuilder = Metadata.builder(metadata); - DecommissionAttributeMetadata newMetadata = decommissionAttributeMetadata.withUpdatedStatus(decommissionStatus); - mdBuilder.putCustom(DecommissionAttributeMetadata.TYPE, newMetadata); - return ClusterState.builder(currentState).metadata(mdBuilder).build(); - } - @Override - public void onFailure(String source, Exception e) { - logger.error(() -> new ParameterizedMessage("failed to mark status as [{}]", decommissionStatus.status()), e); - } - - @Override - public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { - listener.onResponse(new ClusterStateUpdateResponse(true)); - } - - }); - } private Set nodesWithDecommissionAttribute(ClusterState clusterState, DecommissionAttribute decommissionAttribute) { Set nodesWithDecommissionAttribute = new HashSet<>(); @@ -395,16 +365,6 @@ private Set nodesWithDecommissionAttribute(ClusterState clusterSt return nodesWithDecommissionAttribute; } - private static boolean assertIncrementalStatusOrFailed(DecommissionStatus oldStatus, DecommissionStatus newStatus) { - if (oldStatus == null || newStatus.equals(DecommissionStatus.DECOMMISSION_FAILED)) return true; - else if (newStatus.equals(DecommissionStatus.DECOMMISSION_SUCCESSFUL)) { - return oldStatus.equals(DecommissionStatus.DECOMMISSION_IN_PROGRESS); - } else if (newStatus.equals(DecommissionStatus.DECOMMISSION_IN_PROGRESS)) { - return oldStatus.equals(DecommissionStatus.DECOMMISSION_INIT); - } - return true; - } - private static boolean nodeHasDecommissionedAttribute(DiscoveryNode discoveryNode, DecommissionAttribute decommissionAttribute) { return discoveryNode.getAttributes().get(decommissionAttribute.attributeName()).equals(decommissionAttribute.attributeValue()); } diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java new file mode 100644 index 0000000000000..33861b09c2596 --- /dev/null +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java @@ -0,0 +1,179 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.decommission; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.opensearch.Version; +import org.opensearch.action.ActionListener; +import org.opensearch.action.admin.cluster.configuration.TransportAddVotingConfigExclusionsActionTests; +import org.opensearch.cluster.ClusterName; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.ClusterStateObserver; +import org.opensearch.cluster.ClusterStateUpdateTask; +import org.opensearch.cluster.ack.ClusterStateUpdateResponse; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.node.DiscoveryNodeRole; +import org.opensearch.cluster.node.DiscoveryNodes; +import org.opensearch.cluster.routing.allocation.AllocationService; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.test.OpenSearchTestCase; +import org.opensearch.threadpool.TestThreadPool; +import org.opensearch.threadpool.ThreadPool; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static java.util.Collections.singletonMap; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.sameInstance; +import static org.mockito.Mockito.mock; +import static org.opensearch.cluster.ClusterState.builder; +import static org.opensearch.test.ClusterServiceUtils.createClusterService; +import static org.opensearch.test.ClusterServiceUtils.setState; + +public class DecommissionControllerTests extends OpenSearchTestCase { + + private static ThreadPool threadPool; + private static ClusterService clusterService; + private DecommissionController decommissionController; + private ClusterStateObserver clusterStateObserver; + + @BeforeClass + public static void createThreadPoolAndClusterService() { + threadPool = new TestThreadPool("test", Settings.EMPTY); + clusterService = createClusterService(threadPool); + } + + @AfterClass + public static void shutdownThreadPoolAndClusterService() { + clusterService.stop(); + threadPool.shutdown(); + } + + @Before + public void setupForTests() { + ClusterState clusterState = ClusterState.builder(new ClusterName("test")).build(); + logger.info("--> adding five nodes on same zone_1"); + clusterState = addNodes(clusterState, "zone_1", "node1", "node2", "node3", "node4", "node5"); + logger.info("--> adding five nodes on same zone_2"); + clusterState = addNodes(clusterState, "zone_2", "node6", "node7", "node8", "node9", "node10"); + logger.info("--> adding five nodes on same zone_3"); + clusterState = addNodes(clusterState, "zone_3", "node11", "node12", "node13", "node14", "node15"); + + final ClusterState.Builder builder = builder(clusterState); + setState(clusterService, builder); + + final AllocationService allocationService = mock(AllocationService.class); + decommissionController = new DecommissionController(clusterService, allocationService, threadPool); + clusterStateObserver = new ClusterStateObserver(clusterService, null, logger, threadPool.getThreadContext()); + } + + public void testRemoveNodesForDecommissionRequest() throws InterruptedException{ + final CountDownLatch countDownLatch = new CountDownLatch(2); + + Set nodesToBeRemoved = new HashSet<>(); + nodesToBeRemoved.add(clusterService.state().nodes().get("node11")); + nodesToBeRemoved.add(clusterService.state().nodes().get("node12")); + nodesToBeRemoved.add(clusterService.state().nodes().get("node13")); + nodesToBeRemoved.add(clusterService.state().nodes().get("node14")); + nodesToBeRemoved.add(clusterService.state().nodes().get("node15")); + + ActionListener actionListener = new ActionListener() { + @Override + public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { + logger.info("test"); + } + + @Override + public void onFailure(Exception e) { + } + }; + + clusterStateObserver.waitForNextChange(new UpdateClusterStateForDecommission(countDownLatch, nodesToBeRemoved)); + decommissionController.handleNodesDecommissionRequest( + nodesToBeRemoved, + "unit-test", + TimeValue.timeValueSeconds(30L), + actionListener + ); + + assertTrue(countDownLatch.await(30, TimeUnit.SECONDS)); + assertEquals(clusterService.getClusterApplierService().state().nodes().getDataNodes().size(), 10); + } + + private ClusterState addNodes(ClusterState clusterState, String zone, String... nodeIds) { + DiscoveryNodes.Builder nodeBuilder = DiscoveryNodes.builder(clusterState.nodes()); + org.opensearch.common.collect.List.of(nodeIds).forEach(nodeId -> nodeBuilder.add(newNode(nodeId, singletonMap("zone", zone)))); + clusterState = ClusterState.builder(clusterState).nodes(nodeBuilder).build(); + return clusterState; + } + + private static DiscoveryNode newNode(String nodeId, Map attributes) { + return new DiscoveryNode(nodeId, buildNewFakeTransportAddress(), attributes, CLUSTER_MANAGER_DATA_ROLES, Version.CURRENT); + } + + final private static Set CLUSTER_MANAGER_DATA_ROLES = Collections.unmodifiableSet( + new HashSet<>(Arrays.asList(DiscoveryNodeRole.CLUSTER_MANAGER_ROLE, DiscoveryNodeRole.DATA_ROLE)) + ); + + private static class UpdateClusterStateForDecommission implements ClusterStateObserver.Listener { + + final CountDownLatch doneLatch; + final Set discoveryNodes; + + UpdateClusterStateForDecommission(CountDownLatch latch, Set discoveryNodes) { + this.doneLatch = latch; + this.discoveryNodes = discoveryNodes; + } + + @Override + public void onNewClusterState(ClusterState state) { + clusterService.getClusterManagerService().submitStateUpdateTask("decommission", new ClusterStateUpdateTask() { + @Override + public ClusterState execute(ClusterState currentState) throws Exception { + assertThat(currentState, sameInstance(state)); + final DiscoveryNodes.Builder remainingNodesBuilder = DiscoveryNodes.builder(currentState.nodes()); + for (DiscoveryNode nodeToBeRemoved : discoveryNodes) { + remainingNodesBuilder.remove(nodeToBeRemoved); + } + return ClusterState.builder(currentState).nodes(remainingNodesBuilder).build(); + } + + @Override + public void onFailure(String source, Exception e) { + throw new AssertionError("unexpected failure", e); + } + + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + doneLatch.countDown(); + } + }); + } + + @Override + public void onClusterServiceClose() { + throw new AssertionError("unexpected close"); + } + + @Override + public void onTimeout(TimeValue timeout) { + throw new AssertionError("unexpected timeout"); + } + } +} diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionHelperTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionHelperTests.java deleted file mode 100644 index 9452c0e94c262..0000000000000 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionHelperTests.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -package org.opensearch.cluster.decommission; - -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.opensearch.Version; -import org.opensearch.cluster.ClusterName; -import org.opensearch.cluster.ClusterState; -import org.opensearch.cluster.ClusterStateObserver; -import org.opensearch.cluster.coordination.CoordinationMetadata; -import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.cluster.node.DiscoveryNodeRole; -import org.opensearch.cluster.node.DiscoveryNodes; -import org.opensearch.cluster.routing.allocation.AllocationService; -import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.settings.ClusterSettings; -import org.opensearch.common.settings.Settings; -import org.opensearch.test.OpenSearchTestCase; -import org.opensearch.threadpool.TestThreadPool; -import org.opensearch.threadpool.ThreadPool; -import org.opensearch.transport.TransportService; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static java.util.Collections.emptyMap; -import static java.util.Collections.emptySet; -import static java.util.Collections.singletonMap; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import static org.opensearch.test.ClusterServiceUtils.createClusterService; - -public class DecommissionHelperTests extends OpenSearchTestCase { - - private static ThreadPool threadPool; - private static ClusterService clusterService; - - @BeforeClass - public static void createThreadPoolAndClusterService() { - threadPool = new TestThreadPool("test", Settings.EMPTY); - clusterService = createClusterService(threadPool); - } - - @AfterClass - public static void shutdownThreadPoolAndClusterService() { - clusterService.stop(); - threadPool.shutdown(); - } - -// public void testRemoveNodesForDecommissionRequest() { -// final AllocationService allocationService = mock(AllocationService.class); -// final ClusterService clusterService = mock(ClusterService.class); -// -// ClusterState clusterState = ClusterState.builder(new ClusterName("test")).build(); -// -// logger.info("--> adding five nodes on same zone_1"); -// clusterState = addNodes(clusterState, "zone_1", "node1", "node2", "node3", "node4", "node5"); -// -// logger.info("--> adding five nodes on same zone_2"); -// clusterState = addNodes(clusterState, "zone_2", "node6", "node7", "node8", "node9", "node10"); -// -// logger.info("--> adding five nodes on same zone_3"); -// clusterState = addNodes(clusterState, "zone_3", "node11", "node12", "node13", "node14", "node15"); -// -// when(clusterService.state()).thenReturn(clusterState); -// -// final DecommissionHelper decommissionHelper = new DecommissionHelper(clusterService, allocationService); -// -// List nodesToBeRemoved = new ArrayList<>(); -// nodesToBeRemoved.add(clusterState.nodes().get("node11")); -// nodesToBeRemoved.add(clusterState.nodes().get("node12")); -// nodesToBeRemoved.add(clusterState.nodes().get("node13")); -// nodesToBeRemoved.add(clusterState.nodes().get("node14")); -// nodesToBeRemoved.add(clusterState.nodes().get("node15")); -// -// decommissionHelper.handleNodesDecommissionRequest(nodesToBeRemoved, "unit-test"); -// assertEquals((clusterService.state().nodes().getSize()), 10); -// } - - private ClusterState addNodes(ClusterState clusterState, String zone, String... nodeIds) { - DiscoveryNodes.Builder nodeBuilder = DiscoveryNodes.builder(clusterState.nodes()); - org.opensearch.common.collect.List.of(nodeIds).forEach(nodeId -> nodeBuilder.add(newNode(nodeId, singletonMap("zone", zone)))); - clusterState = ClusterState.builder(clusterState).nodes(nodeBuilder).build(); - return clusterState; - } - - private static DiscoveryNode newNode(String nodeId, Map attributes) { - return new DiscoveryNode(nodeId, buildNewFakeTransportAddress(), attributes, CLUSTER_MANAGER_DATA_ROLES, Version.CURRENT); - } - - final private static Set CLUSTER_MANAGER_DATA_ROLES = Collections.unmodifiableSet( - new HashSet<>(Arrays.asList(DiscoveryNodeRole.CLUSTER_MANAGER_ROLE, DiscoveryNodeRole.DATA_ROLE)) - ); -} From 1d7eb376af2ea3b057a4925075e3d31bd038f563 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Mon, 29 Aug 2022 17:54:59 +0530 Subject: [PATCH 141/187] Add UT Signed-off-by: Rishab Nahata --- .../DecommissionControllerTests.java | 71 ++++++++++++------- 1 file changed, 46 insertions(+), 25 deletions(-) diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java index 33861b09c2596..262186d0dca2c 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java @@ -38,35 +38,34 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import static java.util.Collections.emptyMap; +import static java.util.Collections.emptySet; import static java.util.Collections.singletonMap; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.sameInstance; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import static org.opensearch.cluster.ClusterState.builder; +import static org.opensearch.cluster.OpenSearchAllocationTestCase.createAllocationService; import static org.opensearch.test.ClusterServiceUtils.createClusterService; import static org.opensearch.test.ClusterServiceUtils.setState; public class DecommissionControllerTests extends OpenSearchTestCase { - private static ThreadPool threadPool; - private static ClusterService clusterService; + private ThreadPool threadPool; + private ClusterService clusterService; + private AllocationService allocationService; private DecommissionController decommissionController; private ClusterStateObserver clusterStateObserver; - @BeforeClass - public static void createThreadPoolAndClusterService() { + @Override + public void setUp() throws Exception { + super.setUp(); threadPool = new TestThreadPool("test", Settings.EMPTY); clusterService = createClusterService(threadPool); - } - - @AfterClass - public static void shutdownThreadPoolAndClusterService() { - clusterService.stop(); - threadPool.shutdown(); - } - - @Before - public void setupForTests() { + allocationService = createAllocationService(); ClusterState clusterState = ClusterState.builder(new ClusterName("test")).build(); logger.info("--> adding five nodes on same zone_1"); clusterState = addNodes(clusterState, "zone_1", "node1", "node2", "node3", "node4", "node5"); @@ -74,17 +73,15 @@ public void setupForTests() { clusterState = addNodes(clusterState, "zone_2", "node6", "node7", "node8", "node9", "node10"); logger.info("--> adding five nodes on same zone_3"); clusterState = addNodes(clusterState, "zone_3", "node11", "node12", "node13", "node14", "node15"); - + clusterState = setLocalNodeAsClusterManagerNode(clusterState, "node1"); final ClusterState.Builder builder = builder(clusterState); setState(clusterService, builder); - - final AllocationService allocationService = mock(AllocationService.class); decommissionController = new DecommissionController(clusterService, allocationService, threadPool); clusterStateObserver = new ClusterStateObserver(clusterService, null, logger, threadPool.getThreadContext()); } public void testRemoveNodesForDecommissionRequest() throws InterruptedException{ - final CountDownLatch countDownLatch = new CountDownLatch(2); + final CountDownLatch countDownLatch = new CountDownLatch(1); Set nodesToBeRemoved = new HashSet<>(); nodesToBeRemoved.add(clusterService.state().nodes().get("node11")); @@ -96,6 +93,7 @@ public void testRemoveNodesForDecommissionRequest() throws InterruptedException{ ActionListener actionListener = new ActionListener() { @Override public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { + countDownLatch.countDown(); logger.info("test"); } @@ -103,16 +101,16 @@ public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { public void onFailure(Exception e) { } }; - - clusterStateObserver.waitForNextChange(new UpdateClusterStateForDecommission(countDownLatch, nodesToBeRemoved)); decommissionController.handleNodesDecommissionRequest( nodesToBeRemoved, "unit-test", - TimeValue.timeValueSeconds(30L), + TimeValue.timeValueSeconds(29L), actionListener ); + clusterStateObserver.waitForNextChange(new UpdateClusterStateForDecommission(countDownLatch, nodesToBeRemoved)); assertTrue(countDownLatch.await(30, TimeUnit.SECONDS)); + ClusterState state = clusterService.getClusterApplierService().state(); assertEquals(clusterService.getClusterApplierService().state().nodes().getDataNodes().size(), 10); } @@ -123,15 +121,38 @@ private ClusterState addNodes(ClusterState clusterState, String zone, String... return clusterState; } + private ClusterState addClusterManagerNode(ClusterState clusterState, String zone, String nodeId) { + DiscoveryNodes.Builder nodeBuilder = DiscoveryNodes.builder(clusterState.nodes()); + nodeBuilder.add(newClusterManagerNode(nodeId, singletonMap("zone", zone))); + clusterState = ClusterState.builder(clusterState).nodes(nodeBuilder).build(); + return clusterState; + } + + private ClusterState setLocalNodeAsClusterManagerNode(ClusterState clusterState, String nodeId) { + DiscoveryNodes.Builder nodeBuilder = DiscoveryNodes.builder(clusterState.nodes()); + nodeBuilder.localNodeId(nodeId); + nodeBuilder.clusterManagerNodeId(nodeId); + clusterState = ClusterState.builder(clusterState).nodes(nodeBuilder).build(); + return clusterState; + } + private static DiscoveryNode newNode(String nodeId, Map attributes) { - return new DiscoveryNode(nodeId, buildNewFakeTransportAddress(), attributes, CLUSTER_MANAGER_DATA_ROLES, Version.CURRENT); + return new DiscoveryNode(nodeId, buildNewFakeTransportAddress(), attributes, DATA_ROLE, Version.CURRENT); + } + + private static DiscoveryNode newClusterManagerNode(String nodeId, Map attributes) { + return new DiscoveryNode(nodeId, buildNewFakeTransportAddress(), attributes, CLUSTER_MANAGER_ROLE, Version.CURRENT); } - final private static Set CLUSTER_MANAGER_DATA_ROLES = Collections.unmodifiableSet( - new HashSet<>(Arrays.asList(DiscoveryNodeRole.CLUSTER_MANAGER_ROLE, DiscoveryNodeRole.DATA_ROLE)) + final private static Set CLUSTER_MANAGER_ROLE = Collections.unmodifiableSet( + new HashSet<>(Arrays.asList(DiscoveryNodeRole.CLUSTER_MANAGER_ROLE)) + ); + + final private static Set DATA_ROLE = Collections.unmodifiableSet( + new HashSet<>(Arrays.asList(DiscoveryNodeRole.DATA_ROLE)) ); - private static class UpdateClusterStateForDecommission implements ClusterStateObserver.Listener { + private class UpdateClusterStateForDecommission implements ClusterStateObserver.Listener { final CountDownLatch doneLatch; final Set discoveryNodes; From 2a215c1472b394effec4a8f48d9e67be58cf1fe8 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Mon, 29 Aug 2022 21:46:05 +0530 Subject: [PATCH 142/187] Add UT for DecommissionController Signed-off-by: Rishab Nahata --- .../decommission/DecommissionController.java | 28 ++- .../decommission/DecommissionService.java | 48 +++-- .../DecommissionControllerTests.java | 193 ++++++++++-------- 3 files changed, 153 insertions(+), 116 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java index 87fe15f35be38..003fe329da2da 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java @@ -11,6 +11,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; +import org.opensearch.OpenSearchTimeoutException; import org.opensearch.action.ActionListener; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateObserver; @@ -62,7 +63,7 @@ public void handleNodesDecommissionRequest( Set nodesToBeDecommissioned, String reason, TimeValue timeout, - ActionListener nodesRemovedListener + ActionListener nodesRemovedListener ) { final Map nodesDecommissionTasks = new LinkedHashMap<>(); nodesToBeDecommissioned.forEach(discoveryNode -> { @@ -99,7 +100,7 @@ public void handleNodesDecommissionRequest( @Override public void onNewClusterState(ClusterState state) { logger.info("successfully removed all decommissioned nodes [{}] from the cluster", nodesToBeDecommissioned.toString()); - nodesRemovedListener.onResponse(new ClusterStateUpdateResponse(true)); + nodesRemovedListener.onResponse(null); } @Override @@ -110,22 +111,26 @@ public void onClusterServiceClose() { @Override public void onTimeout(TimeValue timeout) { logger.info("timed out while waiting for removal of decommissioned nodes"); - nodesRemovedListener.onResponse(new ClusterStateUpdateResponse(false)); + nodesRemovedListener.onFailure( + new OpenSearchTimeoutException( + "timed out waiting for removal of decommissioned nodes [{}] to take effect", + nodesToBeDecommissioned.toString() + ) + ); } }, allDecommissionedNodesRemovedPredicate); } public void updateMetadataWithDecommissionStatus( DecommissionStatus decommissionStatus, - ActionListener listener + ActionListener listener ) { clusterService.submitStateUpdateTask(decommissionStatus.status(), new ClusterStateUpdateTask(Priority.URGENT) { @Override - public ClusterState execute(ClusterState currentState) throws Exception { + public ClusterState execute(ClusterState currentState) { Metadata metadata = currentState.metadata(); DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.custom(DecommissionAttributeMetadata.TYPE); - assert decommissionAttributeMetadata != null && decommissionAttributeMetadata.decommissionAttribute() != null - : "failed to update status for decommission. metadata doesn't exist or invalid"; + assert decommissionAttributeMetadata != null && decommissionAttributeMetadata.decommissionAttribute() != null; assert assertIncrementalStatusOrFailed(decommissionAttributeMetadata.status(), decommissionStatus); Metadata.Builder mdBuilder = Metadata.builder(metadata); DecommissionAttributeMetadata newMetadata = decommissionAttributeMetadata.withUpdatedStatus(decommissionStatus); @@ -135,19 +140,20 @@ public ClusterState execute(ClusterState currentState) throws Exception { @Override public void onFailure(String source, Exception e) { - logger.error(() -> new ParameterizedMessage("failed to mark status as [{}]", decommissionStatus.status()), e); + listener.onFailure(e); } @Override public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { - listener.onResponse(new ClusterStateUpdateResponse(true)); + DecommissionAttributeMetadata decommissionAttributeMetadata = newState.metadata().custom(DecommissionAttributeMetadata.TYPE); + assert decommissionAttributeMetadata.status().equals(decommissionStatus); + listener.onResponse(null); } - }); } private static boolean assertIncrementalStatusOrFailed(DecommissionStatus oldStatus, DecommissionStatus newStatus) { - if (oldStatus == null || newStatus.equals(DecommissionStatus.DECOMMISSION_FAILED)) return true; + if (newStatus.equals(DecommissionStatus.DECOMMISSION_FAILED)) return true; else if (newStatus.equals(DecommissionStatus.DECOMMISSION_SUCCESSFUL)) { return oldStatus.equals(DecommissionStatus.DECOMMISSION_IN_PROGRESS); } else if (newStatus.equals(DecommissionStatus.DECOMMISSION_IN_PROGRESS)) { diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 971da583eabe5..d935bc02b71d4 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -280,14 +280,14 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS private void initiateGracefulDecommission() { // maybe create a supplier for status update listener? - ActionListener listener = new ActionListener<>() { + ActionListener listener = new ActionListener<>() { @Override - public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { + public void onResponse(Void unused) { logger.info( "updated decommission status to [{}], weighing away awareness attribute for graceful shutdown", DecommissionStatus.DECOMMISSION_IN_PROGRESS ); - failDecommissionedNodes(clusterService.state()); + failDecommissionedNodes(clusterService.getClusterApplierService().state()); } @Override @@ -311,10 +311,13 @@ private void failDecommissionedNodes(ClusterState state) { : "unexpected status encountered while decommissioning nodes"; DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute(); - ActionListener statusUpdateListener = new ActionListener<>() { + ActionListener statusUpdateListener = new ActionListener<>() { @Override - public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { - logger.info("successfully updated decommission status"); + public void onResponse(Void unused) { + logger.info( + "updated decommission status to [{}], decommissioning completed.", + DecommissionStatus.DECOMMISSION_SUCCESSFUL + ); } @Override @@ -323,32 +326,33 @@ public void onFailure(Exception e) { } }; - ActionListener nodesRemovalListener = new ActionListener<>() { - @Override - public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { - DecommissionStatus updateStatusTo = clusterStateUpdateResponse.isAcknowledged() ? - DecommissionStatus.DECOMMISSION_SUCCESSFUL : DecommissionStatus.DECOMMISSION_FAILED; - decommissionController.updateMetadataWithDecommissionStatus(updateStatusTo, statusUpdateListener); - } - - @Override - public void onFailure(Exception e) { - logger.error("error while waiting for decommissioned nodes to be removed", e); - } - }; - // execute nodes decommissioning and wait for it to complete decommissionController.handleNodesDecommissionRequest( nodesWithDecommissionAttribute(state, decommissionAttribute), "nodes-decommissioned", TimeValue.timeValueSeconds(30L), - nodesRemovalListener + new ActionListener() { + @Override + public void onResponse(Void unused) { + decommissionController.updateMetadataWithDecommissionStatus( + DecommissionStatus.DECOMMISSION_SUCCESSFUL, + statusUpdateListener + ); + } + + @Override + public void onFailure(Exception e) { + decommissionController.updateMetadataWithDecommissionStatus( + DecommissionStatus.DECOMMISSION_FAILED, + statusUpdateListener + ); + } + } ); clearVotingConfigAfterSuccessfulDecommission(); } - private Set nodesWithDecommissionAttribute(ClusterState clusterState, DecommissionAttribute decommissionAttribute) { Set nodesWithDecommissionAttribute = new HashSet<>(); final Predicate shouldRemoveNodePredicate = discoveryNode -> nodeHasDecommissionedAttribute( diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java index 262186d0dca2c..809a392d060e2 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java @@ -8,9 +8,11 @@ package org.opensearch.cluster.decommission; +import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; +import org.opensearch.OpenSearchTimeoutException; import org.opensearch.Version; import org.opensearch.action.ActionListener; import org.opensearch.action.admin.cluster.configuration.TransportAddVotingConfigExclusionsActionTests; @@ -19,11 +21,14 @@ import org.opensearch.cluster.ClusterStateObserver; import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; +import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; +import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.node.DiscoveryNodeRole; import org.opensearch.cluster.node.DiscoveryNodes; import org.opensearch.cluster.routing.allocation.AllocationService; import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.collect.ImmutableOpenMap; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; import org.opensearch.test.OpenSearchTestCase; @@ -37,15 +42,21 @@ import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import static java.util.Collections.emptyMap; import static java.util.Collections.emptySet; import static java.util.Collections.singletonMap; import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.sameInstance; +import static org.hamcrest.Matchers.startsWith; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.opensearch.cluster.ClusterState.builder; import static org.opensearch.cluster.OpenSearchAllocationTestCase.createAllocationService; @@ -58,7 +69,6 @@ public class DecommissionControllerTests extends OpenSearchTestCase { private ClusterService clusterService; private AllocationService allocationService; private DecommissionController decommissionController; - private ClusterStateObserver clusterStateObserver; @Override public void setUp() throws Exception { @@ -66,6 +76,10 @@ public void setUp() throws Exception { threadPool = new TestThreadPool("test", Settings.EMPTY); clusterService = createClusterService(threadPool); allocationService = createAllocationService(); + } + + @Before + public void setDefaultClusterState() { ClusterState clusterState = ClusterState.builder(new ClusterName("test")).build(); logger.info("--> adding five nodes on same zone_1"); clusterState = addNodes(clusterState, "zone_1", "node1", "node2", "node3", "node4", "node5"); @@ -75,14 +89,21 @@ public void setUp() throws Exception { clusterState = addNodes(clusterState, "zone_3", "node11", "node12", "node13", "node14", "node15"); clusterState = setLocalNodeAsClusterManagerNode(clusterState, "node1"); final ClusterState.Builder builder = builder(clusterState); - setState(clusterService, builder); + setState( + clusterService, + builder + ); decommissionController = new DecommissionController(clusterService, allocationService, threadPool); - clusterStateObserver = new ClusterStateObserver(clusterService, null, logger, threadPool.getThreadContext()); } - public void testRemoveNodesForDecommissionRequest() throws InterruptedException{ - final CountDownLatch countDownLatch = new CountDownLatch(1); + @After + public void shutdownThreadPoolAndClusterService() { + clusterService.stop(); + threadPool.shutdown(); + } + public void testNodesRemovedForDecommissionRequestSuccessfulResponse() throws InterruptedException { + final CountDownLatch countDownLatch = new CountDownLatch(1); Set nodesToBeRemoved = new HashSet<>(); nodesToBeRemoved.add(clusterService.state().nodes().get("node11")); nodesToBeRemoved.add(clusterService.state().nodes().get("node12")); @@ -90,28 +111,95 @@ public void testRemoveNodesForDecommissionRequest() throws InterruptedException{ nodesToBeRemoved.add(clusterService.state().nodes().get("node14")); nodesToBeRemoved.add(clusterService.state().nodes().get("node15")); - ActionListener actionListener = new ActionListener() { - @Override - public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { - countDownLatch.countDown(); - logger.info("test"); - } + decommissionController.handleNodesDecommissionRequest( + nodesToBeRemoved, + "unit-test", + TimeValue.timeValueSeconds(30L), + new ActionListener() { + @Override + public void onResponse(Void unused) { + countDownLatch.countDown(); + } - @Override - public void onFailure(Exception e) { + @Override + public void onFailure(Exception e) { + fail("there shouldn't have been any failure"); + } } - }; + ); + + assertTrue(countDownLatch.await(30, TimeUnit.SECONDS)); + // test all 5 nodes removed and cluster has 10 nodes + Set nodes = StreamSupport.stream( + clusterService.getClusterApplierService().state().nodes().spliterator(), false + ).collect(Collectors.toSet()); + assertEquals(nodes.size(), 10); + // test no nodes part of zone-3 + for (DiscoveryNode node : nodes) { + assertNotEquals(node.getAttributes().get("zone"), "zone-1"); + } + } + + public void testTimesOut() throws InterruptedException { + final CountDownLatch countDownLatch = new CountDownLatch(1); + Set nodesToBeRemoved = new HashSet<>(); + nodesToBeRemoved.add(clusterService.state().nodes().get("node11")); + nodesToBeRemoved.add(clusterService.state().nodes().get("node12")); + nodesToBeRemoved.add(clusterService.state().nodes().get("node13")); + nodesToBeRemoved.add(clusterService.state().nodes().get("node14")); + nodesToBeRemoved.add(clusterService.state().nodes().get("node15")); decommissionController.handleNodesDecommissionRequest( nodesToBeRemoved, "unit-test", - TimeValue.timeValueSeconds(29L), - actionListener + TimeValue.timeValueMillis(2), + new ActionListener() { + @Override + public void onResponse(Void unused) { + fail("response shouldn't have been called"); + } + + @Override + public void onFailure(Exception e) { + assertThat(e, instanceOf(OpenSearchTimeoutException.class)); + assertThat(e.getMessage(), startsWith("timed out waiting for removal of decommissioned nodes")); + countDownLatch.countDown(); + } + } + ); + assertTrue(countDownLatch.await(30, TimeUnit.SECONDS)); + } + + public void testSuccessfulDecommissionStatusMetadataUpdate() throws InterruptedException { + final CountDownLatch countDownLatch = new CountDownLatch(1); + DecommissionAttributeMetadata oldMetadata = new DecommissionAttributeMetadata( + new DecommissionAttribute("zone", "zone-1"), + DecommissionStatus.DECOMMISSION_IN_PROGRESS ); - clusterStateObserver.waitForNextChange(new UpdateClusterStateForDecommission(countDownLatch, nodesToBeRemoved)); + ClusterState state = clusterService.state(); + Metadata metadata = state.metadata(); + Metadata.Builder mdBuilder = Metadata.builder(metadata); + mdBuilder.putCustom(DecommissionAttributeMetadata.TYPE, oldMetadata); + state = ClusterState.builder(state).metadata(mdBuilder).build(); + setState(clusterService, state); + + decommissionController.updateMetadataWithDecommissionStatus( + DecommissionStatus.DECOMMISSION_SUCCESSFUL, + new ActionListener() { + @Override + public void onResponse(Void unused) { + countDownLatch.countDown(); + } + @Override + public void onFailure(Exception e) { + fail("decommission status update failed"); + } + } + ); assertTrue(countDownLatch.await(30, TimeUnit.SECONDS)); - ClusterState state = clusterService.getClusterApplierService().state(); - assertEquals(clusterService.getClusterApplierService().state().nodes().getDataNodes().size(), 10); + ClusterState newState = clusterService.getClusterApplierService().state(); + DecommissionAttributeMetadata decommissionAttributeMetadata = newState.metadata().custom(DecommissionAttributeMetadata.TYPE); + assertEquals(decommissionAttributeMetadata.status(), DecommissionStatus.DECOMMISSION_SUCCESSFUL); } private ClusterState addNodes(ClusterState clusterState, String zone, String... nodeIds) { @@ -121,13 +209,6 @@ private ClusterState addNodes(ClusterState clusterState, String zone, String... return clusterState; } - private ClusterState addClusterManagerNode(ClusterState clusterState, String zone, String nodeId) { - DiscoveryNodes.Builder nodeBuilder = DiscoveryNodes.builder(clusterState.nodes()); - nodeBuilder.add(newClusterManagerNode(nodeId, singletonMap("zone", zone))); - clusterState = ClusterState.builder(clusterState).nodes(nodeBuilder).build(); - return clusterState; - } - private ClusterState setLocalNodeAsClusterManagerNode(ClusterState clusterState, String nodeId) { DiscoveryNodes.Builder nodeBuilder = DiscoveryNodes.builder(clusterState.nodes()); nodeBuilder.localNodeId(nodeId); @@ -137,64 +218,10 @@ private ClusterState setLocalNodeAsClusterManagerNode(ClusterState clusterState, } private static DiscoveryNode newNode(String nodeId, Map attributes) { - return new DiscoveryNode(nodeId, buildNewFakeTransportAddress(), attributes, DATA_ROLE, Version.CURRENT); + return new DiscoveryNode(nodeId, buildNewFakeTransportAddress(), attributes, CLUSTER_MANAGER_DATA_ROLE, Version.CURRENT); } - private static DiscoveryNode newClusterManagerNode(String nodeId, Map attributes) { - return new DiscoveryNode(nodeId, buildNewFakeTransportAddress(), attributes, CLUSTER_MANAGER_ROLE, Version.CURRENT); - } - - final private static Set CLUSTER_MANAGER_ROLE = Collections.unmodifiableSet( - new HashSet<>(Arrays.asList(DiscoveryNodeRole.CLUSTER_MANAGER_ROLE)) - ); - - final private static Set DATA_ROLE = Collections.unmodifiableSet( - new HashSet<>(Arrays.asList(DiscoveryNodeRole.DATA_ROLE)) + final private static Set CLUSTER_MANAGER_DATA_ROLE = Collections.unmodifiableSet( + new HashSet<>(Arrays.asList(DiscoveryNodeRole.CLUSTER_MANAGER_ROLE, DiscoveryNodeRole.DATA_ROLE)) ); - - private class UpdateClusterStateForDecommission implements ClusterStateObserver.Listener { - - final CountDownLatch doneLatch; - final Set discoveryNodes; - - UpdateClusterStateForDecommission(CountDownLatch latch, Set discoveryNodes) { - this.doneLatch = latch; - this.discoveryNodes = discoveryNodes; - } - - @Override - public void onNewClusterState(ClusterState state) { - clusterService.getClusterManagerService().submitStateUpdateTask("decommission", new ClusterStateUpdateTask() { - @Override - public ClusterState execute(ClusterState currentState) throws Exception { - assertThat(currentState, sameInstance(state)); - final DiscoveryNodes.Builder remainingNodesBuilder = DiscoveryNodes.builder(currentState.nodes()); - for (DiscoveryNode nodeToBeRemoved : discoveryNodes) { - remainingNodesBuilder.remove(nodeToBeRemoved); - } - return ClusterState.builder(currentState).nodes(remainingNodesBuilder).build(); - } - - @Override - public void onFailure(String source, Exception e) { - throw new AssertionError("unexpected failure", e); - } - - @Override - public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { - doneLatch.countDown(); - } - }); - } - - @Override - public void onClusterServiceClose() { - throw new AssertionError("unexpected close"); - } - - @Override - public void onTimeout(TimeValue timeout) { - throw new AssertionError("unexpected timeout"); - } - } } From 99eb1f44da93b9340ff0846811e33ba6fddf01c5 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 30 Aug 2022 12:05:10 +0530 Subject: [PATCH 143/187] Improvements and UTs Signed-off-by: Rishab Nahata --- .../AddVotingConfigExclusionsRequest.java | 2 +- .../decommission/DecommissionController.java | 80 +++++++- .../decommission/DecommissionService.java | 186 +++++++++--------- .../DecommissionControllerTests.java | 112 ++++++++++- .../DecommissionServiceTests.java | 172 ++++++++++++++++ 5 files changed, 455 insertions(+), 97 deletions(-) diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/configuration/AddVotingConfigExclusionsRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/configuration/AddVotingConfigExclusionsRequest.java index a2a77a1316898..739bfaf2a3fb1 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/configuration/AddVotingConfigExclusionsRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/configuration/AddVotingConfigExclusionsRequest.java @@ -157,7 +157,7 @@ Set resolveVotingConfigExclusions(ClusterState currentSta } else { assert nodeNames.length >= 1; Map existingNodes = StreamSupport.stream(allNodes.spliterator(), false) - .collect(Collectors.toMap(DiscoveryNode::getName, Function.identity())); + .collect(Collectors.toMap(DiscoveryNode::getName, Function.identity(), (r1, r2) -> r1)); for (String nodeName : nodeNames) { if (existingNodes.containsKey(nodeName)) { diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java index 003fe329da2da..d699fc51e819a 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java @@ -13,6 +13,12 @@ import org.apache.logging.log4j.message.ParameterizedMessage; import org.opensearch.OpenSearchTimeoutException; import org.opensearch.action.ActionListener; +import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsAction; +import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsRequest; +import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsResponse; +import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsAction; +import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsRequest; +import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsResponse; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateObserver; import org.opensearch.cluster.ClusterStateTaskConfig; @@ -26,17 +32,24 @@ import org.opensearch.cluster.routing.allocation.AllocationService; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Priority; +import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.unit.TimeValue; import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.Transport; +import org.opensearch.transport.TransportException; +import org.opensearch.transport.TransportResponseHandler; +import org.opensearch.transport.TransportService; +import java.io.IOException; import java.util.Iterator; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Predicate; /** - * Helper executor class to remove list of nodes from the cluster + * Helper controller class to remove list of nodes from the cluster and update status * * @opensearch.internal */ @@ -47,18 +60,83 @@ public class DecommissionController { private final NodeRemovalClusterStateTaskExecutor nodeRemovalExecutor; private final ClusterService clusterService; + private final TransportService transportService; private final ThreadPool threadPool; DecommissionController( ClusterService clusterService, + TransportService transportService, AllocationService allocationService, ThreadPool threadPool ) { this.clusterService = clusterService; + this.transportService = transportService; this.nodeRemovalExecutor = new NodeRemovalClusterStateTaskExecutor(allocationService, logger); this.threadPool = threadPool; } + public void excludeDecommissionedNodesFromVotingConfig(Set nodes, ActionListener listener) { + transportService.sendRequest( + transportService.getLocalNode(), + AddVotingConfigExclusionsAction.NAME, + new AddVotingConfigExclusionsRequest(nodes.stream().toArray(String[] :: new)), + new TransportResponseHandler() { + @Override + public void handleResponse(AddVotingConfigExclusionsResponse response) { + listener.onResponse(null); + } + + @Override + public void handleException(TransportException exp) { + listener.onFailure(exp); + } + + @Override + public String executor() { + return ThreadPool.Names.SAME; + } + + @Override + public AddVotingConfigExclusionsResponse read(StreamInput in) throws IOException { + return new AddVotingConfigExclusionsResponse(in); + } + } + ); + } + + public void clearVotingConfigExclusion(ActionListener listener) { + final ClearVotingConfigExclusionsRequest clearVotingConfigExclusionsRequest = new ClearVotingConfigExclusionsRequest(); + clearVotingConfigExclusionsRequest.setWaitForRemoval(true); + transportService.sendRequest( + transportService.getLocalNode(), + ClearVotingConfigExclusionsAction.NAME, + clearVotingConfigExclusionsRequest, + new TransportResponseHandler() { + @Override + public void handleResponse(ClearVotingConfigExclusionsResponse response) { + logger.info("successfully cleared voting config after decommissioning"); + listener.onResponse(null); + } + + @Override + public void handleException(TransportException exp) { + logger.debug(new ParameterizedMessage("failure in clearing voting config exclusion after decommissioning"), exp); + listener.onFailure(exp); + } + + @Override + public String executor() { + return ThreadPool.Names.SAME; + } + + @Override + public ClearVotingConfigExclusionsResponse read(StreamInput in) throws IOException { + return new ClearVotingConfigExclusionsResponse(in); + } + } + ); + } + public void handleNodesDecommissionRequest( Set nodesToBeDecommissioned, String reason, diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index d935bc02b71d4..729ec944560d4 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -12,16 +12,11 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; import org.opensearch.action.ActionListener; -import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsAction; -import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsRequest; -import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsResponse; -import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsAction; -import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsRequest; -import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsResponse; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.NotClusterManagerException; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; +import org.opensearch.cluster.coordination.CoordinationMetadata; import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; @@ -38,14 +33,20 @@ import org.opensearch.transport.TransportException; import org.opensearch.transport.TransportResponseHandler; import org.opensearch.transport.TransportService; +import org.opensearch.cluster.coordination.CoordinationMetadata.VotingConfigExclusion; import java.io.IOException; -import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.function.Predicate; +import java.util.stream.Collectors; + +import static org.opensearch.cluster.routing.allocation.decider.AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING; +import static org.opensearch.cluster.routing.allocation.decider.AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_FORCE_GROUP_SETTING; /** * Service responsible for entire lifecycle of decommissioning and recommissioning an awareness attribute. @@ -72,6 +73,7 @@ public class DecommissionService { private final ThreadPool threadPool; private final DecommissionController decommissionController; private volatile List awarenessAttributes; + private volatile Map> forcedAwarenessAttributes; @Inject public DecommissionService( @@ -87,110 +89,100 @@ public DecommissionService( this.threadPool = threadPool; this.decommissionController = new DecommissionController( clusterService, + transportService, allocationService, threadPool ); - this.awarenessAttributes = AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING.get(settings); + this.awarenessAttributes = CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING.get(settings); clusterSettings.addSettingsUpdateConsumer( - AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING, + CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING, this::setAwarenessAttributes ); - } - List getAwarenessAttributes() { - return awarenessAttributes; + setForcedAwarenessAttributes(CLUSTER_ROUTING_ALLOCATION_AWARENESS_FORCE_GROUP_SETTING.get(settings)); + clusterSettings.addSettingsUpdateConsumer( + CLUSTER_ROUTING_ALLOCATION_AWARENESS_FORCE_GROUP_SETTING, + this::setForcedAwarenessAttributes + ); } private void setAwarenessAttributes(List awarenessAttributes) { this.awarenessAttributes = awarenessAttributes; } + private void setForcedAwarenessAttributes(Settings forceSettings) { + Map> forcedAwarenessAttributes = new HashMap<>(); + Map forceGroups = forceSettings.getAsGroups(); + for (Map.Entry entry : forceGroups.entrySet()) { + List aValues = entry.getValue().getAsList("values"); + if (aValues.size() > 0) { + forcedAwarenessAttributes.put(entry.getKey(), aValues); + } + } + this.forcedAwarenessAttributes = forcedAwarenessAttributes; + } + public void initiateAttributeDecommissioning( final DecommissionAttribute decommissionAttribute, final ActionListener listener, ClusterState state ) { - validateAwarenessAttribute(decommissionAttribute, getAwarenessAttributes()); + validateAwarenessAttribute(decommissionAttribute, awarenessAttributes, forcedAwarenessAttributes); logger.info("initiating awareness attribute [{}] decommissioning", decommissionAttribute.toString()); + + // remove all decommissioned cluster manager eligible nodes from voting config + // The method ensures that we don't exclude nodes multiple times excludeDecommissionedClusterManagerNodesFromVotingConfig(decommissionAttribute); - registerDecommissionAttribute(decommissionAttribute, listener); + + // explicitly throwing NotClusterManagerException as we can certainly say the local cluster manager node will + // be abdicated and soon will no longer be cluster manager. + if(transportService.getLocalNode().isClusterManagerNode() + && !nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute)) { + registerDecommissionAttribute(decommissionAttribute, listener); + } else { + throw new NotClusterManagerException( + "node [" + + transportService.getLocalNode().toString() + + "] not eligible to execute decommission request. Will retry until timeout." + ); + } } private void excludeDecommissionedClusterManagerNodesFromVotingConfig(DecommissionAttribute decommissionAttribute) { - final Predicate shouldDecommissionPredicate = discoveryNode -> nodeHasDecommissionedAttribute( - discoveryNode, - decommissionAttribute + Set clusterManagerNodesToBeDecommissioned = nodesWithDecommissionAttribute( + clusterService.state(), decommissionAttribute, true ); - List clusterManagerNodesToBeDecommissioned = new ArrayList<>(); - Iterator clusterManagerNodesIter = clusterService.state().nodes().getClusterManagerNodes().valuesIt(); - while (clusterManagerNodesIter.hasNext()) { - final DiscoveryNode node = clusterManagerNodesIter.next(); - if (shouldDecommissionPredicate.test(node)) { - clusterManagerNodesToBeDecommissioned.add(node.getName()); - } + Set clusterManagerNodesNameToBeDecommissioned = clusterManagerNodesToBeDecommissioned.stream() + .map(DiscoveryNode::getName) + .collect(Collectors.toSet()); + + Set currentVotingConfigExclusions = clusterService.getClusterApplierService().state().coordinationMetadata().getVotingConfigExclusions(); + Set excludedNodesName = currentVotingConfigExclusions.stream().map(VotingConfigExclusion::getNodeName).collect(Collectors.toSet()); + + // check if the to-be-excluded nodes are excluded. If yes, we don't need to exclude them again + if (clusterManagerNodesNameToBeDecommissioned.size() == 0 + || (clusterManagerNodesNameToBeDecommissioned.size() == excludedNodesName.size() + && excludedNodesName.containsAll(clusterManagerNodesNameToBeDecommissioned))) { + return; } - transportService.sendRequest( - transportService.getLocalNode(), - AddVotingConfigExclusionsAction.NAME, - new AddVotingConfigExclusionsRequest(clusterManagerNodesToBeDecommissioned.toArray(String[]::new)), - new TransportResponseHandler() { + decommissionController.excludeDecommissionedNodesFromVotingConfig( + clusterManagerNodesNameToBeDecommissioned, + new ActionListener() { @Override - public void handleResponse(AddVotingConfigExclusionsResponse response) { + public void onResponse(Void unused) { logger.info( - "successfully removed decommissioned cluster manager eligible nodes [{}] from voting config, " - + "proceeding to drain the decommissioned nodes", - clusterManagerNodesToBeDecommissioned.toString() + "successfully removed decommissioned cluster manager eligible nodes [{}] from voting config " + , clusterManagerNodesToBeDecommissioned.toString() ); } @Override - public void handleException(TransportException exp) { + public void onFailure(Exception e) { logger.debug( new ParameterizedMessage("failure in removing decommissioned cluster manager eligible nodes from voting config"), - exp + e ); } - - @Override - public String executor() { - return ThreadPool.Names.SAME; - } - - @Override - public AddVotingConfigExclusionsResponse read(StreamInput in) throws IOException { - return new AddVotingConfigExclusionsResponse(in); - } - } - ); - } - - private void clearVotingConfigAfterSuccessfulDecommission() { - final ClearVotingConfigExclusionsRequest clearVotingConfigExclusionsRequest = new ClearVotingConfigExclusionsRequest(); - clearVotingConfigExclusionsRequest.setWaitForRemoval(true); - transportService.sendRequest( - transportService.getLocalNode(), - ClearVotingConfigExclusionsAction.NAME, - clearVotingConfigExclusionsRequest, - new TransportResponseHandler() { - @Override - public void handleResponse(ClearVotingConfigExclusionsResponse response) { - logger.info("successfully cleared voting config after decommissioning"); - } - - @Override - public void handleException(TransportException exp) { - logger.debug(new ParameterizedMessage("failure in clearing voting config exclusion after decommissioning"), exp); - } - - @Override - public String executor() { - return ThreadPool.Names.SAME; - } - - @Override - public ClearVotingConfigExclusionsResponse read(StreamInput in) throws IOException { - return new ClearVotingConfigExclusionsResponse(in); - } } ); } @@ -210,14 +202,6 @@ private void registerDecommissionAttribute( final DecommissionAttribute decommissionAttribute, final ActionListener listener ) { - if (!transportService.getLocalNode().isClusterManagerNode() - || nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute)) { - throw new NotClusterManagerException( - "node [" - + transportService.getLocalNode().toString() - + "] not eligible to execute decommission request. Will retry until timeout." - ); - } clusterService.submitStateUpdateTask( "put_decommission [" + decommissionAttribute + "]", new ClusterStateUpdateTask(Priority.URGENT) { @@ -328,7 +312,7 @@ public void onFailure(Exception e) { // execute nodes decommissioning and wait for it to complete decommissionController.handleNodesDecommissionRequest( - nodesWithDecommissionAttribute(state, decommissionAttribute), + nodesWithDecommissionAttribute(state, decommissionAttribute, false), "nodes-decommissioned", TimeValue.timeValueSeconds(30L), new ActionListener() { @@ -349,20 +333,25 @@ public void onFailure(Exception e) { } } ); - clearVotingConfigAfterSuccessfulDecommission(); +// decommissionController.clearVotingConfigExclusion(); } - - private Set nodesWithDecommissionAttribute(ClusterState clusterState, DecommissionAttribute decommissionAttribute) { + public Set nodesWithDecommissionAttribute( + ClusterState clusterState, + DecommissionAttribute decommissionAttribute, + boolean onlyClusterManagerNodes + ) { Set nodesWithDecommissionAttribute = new HashSet<>(); - final Predicate shouldRemoveNodePredicate = discoveryNode -> nodeHasDecommissionedAttribute( + final Predicate shouldDecommissionNodePredicate = discoveryNode -> nodeHasDecommissionedAttribute( discoveryNode, decommissionAttribute ); - Iterator nodesIter = clusterState.nodes().getNodes().valuesIt(); + Iterator nodesIter = onlyClusterManagerNodes? clusterState.nodes().getClusterManagerNodes().valuesIt() : + clusterState.nodes().getNodes().valuesIt(); + while (nodesIter.hasNext()) { final DiscoveryNode node = nodesIter.next(); - if (shouldRemoveNodePredicate.test(node)) { + if (shouldDecommissionNodePredicate.test(node)) { nodesWithDecommissionAttribute.add(node); } } @@ -373,11 +362,22 @@ private static boolean nodeHasDecommissionedAttribute(DiscoveryNode discoveryNod return discoveryNode.getAttributes().get(decommissionAttribute.attributeName()).equals(decommissionAttribute.attributeValue()); } - private static void validateAwarenessAttribute(final DecommissionAttribute decommissionAttribute, List awarenessAttributes) { + private static void validateAwarenessAttribute( + final DecommissionAttribute decommissionAttribute, + List awarenessAttributes, + Map> forcedAwarenessAttributes) { + if (awarenessAttributes == null || forcedAwarenessAttributes == null) { + throw new DecommissionFailedException(decommissionAttribute, "awareness attribute and forced awareness attribute not set to the cluster."); + } if (!awarenessAttributes.contains(decommissionAttribute.attributeName())) { throw new DecommissionFailedException(decommissionAttribute, "invalid awareness attribute requested for decommissioning"); } - // TODO - should attribute value be part of force zone values? If yes, read setting and throw exception if not found + if (!forcedAwarenessAttributes.get(decommissionAttribute.attributeName()).contains(decommissionAttribute.attributeValue()) ) { + throw new DecommissionFailedException( + decommissionAttribute, + "invalid awareness attribute value requested for decommissioning. Set forced awareness values before to decommission" + ); + } } private static void ensureNoAwarenessAttributeDecommissioned( diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java index 809a392d060e2..0f7f76fc5eeec 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java @@ -15,13 +15,19 @@ import org.opensearch.OpenSearchTimeoutException; import org.opensearch.Version; import org.opensearch.action.ActionListener; +import org.opensearch.action.admin.cluster.configuration.TransportAddVotingConfigExclusionsAction; import org.opensearch.action.admin.cluster.configuration.TransportAddVotingConfigExclusionsActionTests; +import org.opensearch.action.admin.cluster.configuration.TransportClearVotingConfigExclusionsAction; +import org.opensearch.action.support.ActionFilters; import org.opensearch.cluster.ClusterName; import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.coordination.CoordinationMetadata.VotingConfigExclusion; import org.opensearch.cluster.ClusterStateObserver; import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; +import org.opensearch.cluster.coordination.CoordinationMetadata; import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.node.DiscoveryNodeRole; @@ -29,15 +35,21 @@ import org.opensearch.cluster.routing.allocation.AllocationService; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.collect.ImmutableOpenMap; +import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; +import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.test.OpenSearchTestCase; +import org.opensearch.test.transport.MockTransport; import org.opensearch.threadpool.TestThreadPool; import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportService; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; @@ -49,6 +61,7 @@ import static java.util.Collections.emptySet; import static java.util.Collections.singletonMap; import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.sameInstance; import static org.hamcrest.Matchers.startsWith; @@ -58,6 +71,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.opensearch.action.admin.cluster.configuration.TransportAddVotingConfigExclusionsAction.MAXIMUM_VOTING_CONFIG_EXCLUSIONS_SETTING; import static org.opensearch.cluster.ClusterState.builder; import static org.opensearch.cluster.OpenSearchAllocationTestCase.createAllocationService; import static org.opensearch.test.ClusterServiceUtils.createClusterService; @@ -67,8 +81,10 @@ public class DecommissionControllerTests extends OpenSearchTestCase { private ThreadPool threadPool; private ClusterService clusterService; + private TransportService transportService; private AllocationService allocationService; private DecommissionController decommissionController; + private ClusterSettings clusterSettings; @Override public void setUp() throws Exception { @@ -79,7 +95,7 @@ public void setUp() throws Exception { } @Before - public void setDefaultClusterState() { + public void setTransportServiceAndDefaultClusterState() { ClusterState clusterState = ClusterState.builder(new ClusterName("test")).build(); logger.info("--> adding five nodes on same zone_1"); clusterState = addNodes(clusterState, "zone_1", "node1", "node2", "node3", "node4", "node5"); @@ -88,12 +104,47 @@ public void setDefaultClusterState() { logger.info("--> adding five nodes on same zone_3"); clusterState = addNodes(clusterState, "zone_3", "node11", "node12", "node13", "node14", "node15"); clusterState = setLocalNodeAsClusterManagerNode(clusterState, "node1"); + clusterState = setThreeNodesInVotingConfig(clusterState); final ClusterState.Builder builder = builder(clusterState); setState( clusterService, builder ); - decommissionController = new DecommissionController(clusterService, allocationService, threadPool); + final MockTransport transport = new MockTransport(); + transportService = transport.createTransportService( + Settings.EMPTY, + threadPool, + TransportService.NOOP_TRANSPORT_INTERCEPTOR, + boundTransportAddress -> clusterService.state().nodes().get("node1"), + null, + emptySet() + ); + + final Settings.Builder nodeSettingsBuilder = Settings.builder(); + final Settings nodeSettings = nodeSettingsBuilder.build(); + clusterSettings = new ClusterSettings(nodeSettings, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS); + + new TransportAddVotingConfigExclusionsAction( + nodeSettings, + clusterSettings, + transportService, + clusterService, + threadPool, + new ActionFilters(emptySet()), + new IndexNameExpressionResolver(new ThreadContext(Settings.EMPTY)) + ); // registers action + + new TransportClearVotingConfigExclusionsAction( + transportService, + clusterService, + threadPool, + new ActionFilters(emptySet()), + new IndexNameExpressionResolver(new ThreadContext(Settings.EMPTY)) + ); // registers action + + transportService.start(); + transportService.acceptIncomingRequests(); + decommissionController = new DecommissionController(clusterService, transportService, allocationService, threadPool); } @After @@ -102,6 +153,48 @@ public void shutdownThreadPoolAndClusterService() { threadPool.shutdown(); } + public void testAddNodesToVotingConfigExclusion() throws InterruptedException { + final CountDownLatch countDownLatch = new CountDownLatch(1); + Set nodesToRemoveFromVotingConfig = Collections.singleton(randomFrom("node1", "node6", "node11")); + decommissionController.excludeDecommissionedNodesFromVotingConfig( + nodesToRemoveFromVotingConfig, + new ActionListener() { + @Override + public void onResponse(Void unused) { + countDownLatch.countDown(); + } + + @Override + public void onFailure(Exception e) { + fail("unexpected failure occurred while removing node from voting config " + e); + } + } + ); + assertTrue(countDownLatch.await(30, TimeUnit.SECONDS)); + clusterService.getClusterApplierService().state().getVotingConfigExclusions().forEach(vce -> { + assertTrue(nodesToRemoveFromVotingConfig.contains(vce.getNodeName())); + assertEquals(nodesToRemoveFromVotingConfig.size(), 1); + }); + } + + public void testClearVotingConfigExclusions() throws InterruptedException { + final CountDownLatch countDownLatch = new CountDownLatch(1); + decommissionController.clearVotingConfigExclusion(new ActionListener() { + @Override + public void onResponse(Void unused) { + countDownLatch.countDown(); + } + + @Override + public void onFailure(Exception e) { + fail("unexpected failure occurred while clearing voting config exclusion" + e); + } + }); + assertTrue(countDownLatch.await(30, TimeUnit.SECONDS)); + assertThat(clusterService.getClusterApplierService().state().getVotingConfigExclusions(), empty()); + } + + public void testNodesRemovedForDecommissionRequestSuccessfulResponse() throws InterruptedException { final CountDownLatch countDownLatch = new CountDownLatch(1); Set nodesToBeRemoved = new HashSet<>(); @@ -217,6 +310,21 @@ private ClusterState setLocalNodeAsClusterManagerNode(ClusterState clusterState, return clusterState; } + private ClusterState setThreeNodesInVotingConfig(ClusterState clusterState) { + final CoordinationMetadata.VotingConfiguration votingConfiguration = CoordinationMetadata.VotingConfiguration.of( + clusterState.nodes().get("node1"), clusterState.nodes().get("node6"), clusterState.nodes().get("node11") + ); + + Metadata.Builder builder = Metadata.builder().coordinationMetadata( + CoordinationMetadata.builder() + .lastAcceptedConfiguration(votingConfiguration) + .lastCommittedConfiguration(votingConfiguration) + .build() + ); + clusterState = ClusterState.builder(clusterState).metadata(builder).build(); + return clusterState; + } + private static DiscoveryNode newNode(String nodeId, Map attributes) { return new DiscoveryNode(nodeId, buildNewFakeTransportAddress(), attributes, CLUSTER_MANAGER_DATA_ROLE, Version.CURRENT); } diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java index ea4aee984df98..f2baea304344a 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java @@ -8,7 +8,179 @@ package org.opensearch.cluster.decommission; +import org.hamcrest.Matchers; +import org.junit.After; +import org.junit.Before; +import org.opensearch.Version; +import org.opensearch.action.ActionListener; +import org.opensearch.action.search.CreatePitController; +import org.opensearch.cluster.ClusterName; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.ClusterStateObserver; +import org.opensearch.cluster.ack.ClusterStateUpdateResponse; +import org.opensearch.cluster.coordination.CoordinationMetadata; +import org.opensearch.cluster.metadata.Metadata; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.node.DiscoveryNodeRole; +import org.opensearch.cluster.node.DiscoveryNodes; +import org.opensearch.cluster.routing.allocation.AllocationService; +import org.opensearch.cluster.routing.allocation.decider.AwarenessAllocationDecider; +import org.opensearch.cluster.routing.allocation.decider.ClusterRebalanceAllocationDecider; +import org.opensearch.cluster.routing.allocation.decider.ThrottlingAllocationDecider; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.settings.ClusterSettings; +import org.opensearch.common.settings.Settings; import org.opensearch.test.OpenSearchTestCase; +import org.opensearch.test.transport.MockTransport; +import org.opensearch.threadpool.TestThreadPool; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportService; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import static java.util.Collections.emptySet; +import static java.util.Collections.singletonMap; +import static org.mockito.ArgumentMatchers.startsWith; +import static org.mockito.Mockito.mock; +import static org.opensearch.cluster.ClusterState.builder; +import static org.opensearch.cluster.OpenSearchAllocationTestCase.createAllocationService; +import static org.opensearch.cluster.coordination.NoClusterManagerBlockService.NO_CLUSTER_MANAGER_BLOCK_SETTING; +import static org.opensearch.cluster.routing.allocation.decider.AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING; +import static org.opensearch.test.ClusterServiceUtils.createClusterService; +import static org.opensearch.test.ClusterServiceUtils.setState; public class DecommissionServiceTests extends OpenSearchTestCase { + + private ThreadPool threadPool; + private ClusterService clusterService; + private TransportService transportService; + private AllocationService allocationService; + private DecommissionService decommissionService; + private ClusterSettings clusterSettings; + + @Override + public void setUp() throws Exception { + super.setUp(); + super.setUp(); + threadPool = new TestThreadPool("test", Settings.EMPTY); + clusterService = createClusterService(threadPool); + allocationService = createAllocationService(); + } + + @Before + public void setUpService() { + ClusterState clusterState = ClusterState.builder(new ClusterName("test")).build(); + logger.info("--> adding five nodes on same zone_1"); + clusterState = addNodes(clusterState, "zone_1", "node1", "node2", "node3", "node4", "node5"); + logger.info("--> adding five nodes on same zone_2"); + clusterState = addNodes(clusterState, "zone_2", "node6", "node7", "node8", "node9", "node10"); + logger.info("--> adding five nodes on same zone_3"); + clusterState = addNodes(clusterState, "zone_3", "node11", "node12", "node13", "node14", "node15"); + clusterState = setLocalNodeAsClusterManagerNode(clusterState, "node1"); + clusterState = setThreeNodesInVotingConfig(clusterState); + final ClusterState.Builder builder = builder(clusterState); + setState( + clusterService, + builder + ); + final MockTransport transport = new MockTransport(); + transportService = transport.createTransportService( + Settings.EMPTY, + threadPool, + TransportService.NOOP_TRANSPORT_INTERCEPTOR, + boundTransportAddress -> clusterService.state().nodes().get("node1"), + null, + emptySet() + ); + + final Settings.Builder nodeSettingsBuilder = Settings.builder() + .put(AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING.getKey(), "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "zone_1,zone_2,zone_3"); + + clusterSettings = new ClusterSettings(nodeSettingsBuilder.build(), ClusterSettings.BUILT_IN_CLUSTER_SETTINGS); + transportService.start(); + transportService.acceptIncomingRequests(); + + this.decommissionService = new DecommissionService( + nodeSettingsBuilder.build(), + clusterSettings, + clusterService, + transportService, + threadPool, + allocationService + ); + } + + @After + public void shutdownThreadPoolAndClusterService() { + clusterService.stop(); + threadPool.shutdown(); + } + + @SuppressWarnings("unchecked") + public void testDecommissioningNotInitiatedForInvalidAttributeName() { + DecommissionAttribute decommissionAttribute = new DecommissionAttribute("rack", "rack-a"); + ActionListener listener = mock(ActionListener.class); + DecommissionFailedException e = expectThrows(DecommissionFailedException.class, () -> { + decommissionService.initiateAttributeDecommissioning( + decommissionAttribute, listener, clusterService.state()); + }); + assertThat(e.getMessage(), Matchers.endsWith("invalid awareness attribute requested for decommissioning")); + } + + @SuppressWarnings("unchecked") + public void testDecommissioningNotInitiatedForInvalidAttributeValue() { + DecommissionAttribute decommissionAttribute = new DecommissionAttribute("zone", "random"); + ActionListener listener = mock(ActionListener.class); + DecommissionFailedException e = expectThrows(DecommissionFailedException.class, () -> { + decommissionService.initiateAttributeDecommissioning( + decommissionAttribute, listener, clusterService.state()); + }); + assertThat( + e.getMessage(), + Matchers.endsWith("invalid awareness attribute value requested for decommissioning. Set forced awareness values before to decommission") + ); + } + + private ClusterState addNodes(ClusterState clusterState, String zone, String... nodeIds) { + DiscoveryNodes.Builder nodeBuilder = DiscoveryNodes.builder(clusterState.nodes()); + org.opensearch.common.collect.List.of(nodeIds).forEach(nodeId -> nodeBuilder.add(newNode(nodeId, singletonMap("zone", zone)))); + clusterState = ClusterState.builder(clusterState).nodes(nodeBuilder).build(); + return clusterState; + } + + private ClusterState setLocalNodeAsClusterManagerNode(ClusterState clusterState, String nodeId) { + DiscoveryNodes.Builder nodeBuilder = DiscoveryNodes.builder(clusterState.nodes()); + nodeBuilder.localNodeId(nodeId); + nodeBuilder.clusterManagerNodeId(nodeId); + clusterState = ClusterState.builder(clusterState).nodes(nodeBuilder).build(); + return clusterState; + } + + private ClusterState setThreeNodesInVotingConfig(ClusterState clusterState) { + final CoordinationMetadata.VotingConfiguration votingConfiguration = CoordinationMetadata.VotingConfiguration.of( + clusterState.nodes().get("node1"), clusterState.nodes().get("node6"), clusterState.nodes().get("node11") + ); + + Metadata.Builder builder = Metadata.builder().coordinationMetadata( + CoordinationMetadata.builder() + .lastAcceptedConfiguration(votingConfiguration) + .lastCommittedConfiguration(votingConfiguration) + .build() + ); + clusterState = ClusterState.builder(clusterState).metadata(builder).build(); + return clusterState; + } + + private static DiscoveryNode newNode(String nodeId, Map attributes) { + return new DiscoveryNode(nodeId, buildNewFakeTransportAddress(), attributes, CLUSTER_MANAGER_DATA_ROLE, Version.CURRENT); + } + + final private static Set CLUSTER_MANAGER_DATA_ROLE = Collections.unmodifiableSet( + new HashSet<>(Arrays.asList(DiscoveryNodeRole.CLUSTER_MANAGER_ROLE, DiscoveryNodeRole.DATA_ROLE)) + ); } From ea3e495a7f5035bfd7ed77178d3d561469705f32 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 30 Aug 2022 12:54:53 +0530 Subject: [PATCH 144/187] Add UT Signed-off-by: Rishab Nahata --- .../decommission/DecommissionService.java | 38 ++++++++++++------- .../DecommissionServiceTests.java | 25 ++++++++++++ 2 files changed, 50 insertions(+), 13 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 729ec944560d4..5e86954cc18b2 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -128,10 +128,13 @@ public void initiateAttributeDecommissioning( ClusterState state ) { validateAwarenessAttribute(decommissionAttribute, awarenessAttributes, forcedAwarenessAttributes); + DecommissionAttributeMetadata decommissionAttributeMetadata = state.metadata().custom(DecommissionAttributeMetadata.TYPE); + ensureNoAwarenessAttributeDecommissioned(decommissionAttributeMetadata, decommissionAttribute); + logger.info("initiating awareness attribute [{}] decommissioning", decommissionAttribute.toString()); // remove all decommissioned cluster manager eligible nodes from voting config - // The method ensures that we don't exclude nodes multiple times + // The method ensures that we don't exclude same nodes multiple times excludeDecommissionedClusterManagerNodesFromVotingConfig(decommissionAttribute); // explicitly throwing NotClusterManagerException as we can certainly say the local cluster manager node will @@ -190,10 +193,10 @@ public void onFailure(Exception e) { /** * Registers new decommissioned attribute metadata in the cluster state with {@link DecommissionStatus#DECOMMISSION_INIT} *

- * This method can be only called on the cluster-manager node. It tries to create a new decommissioned attribute on the master + * This method can be only called on the cluster-manager node. It tries to create a new decommissioned attribute on the cluster manager * and if it was successful it adds new decommissioned attribute to cluster metadata. *

- * This method ensures that request is performed only on eligible cluster manager node + * This method would only be executed on eligible cluster manager node * * @param decommissionAttribute register decommission attribute in the metadata request * @param listener register decommission listener @@ -365,18 +368,27 @@ private static boolean nodeHasDecommissionedAttribute(DiscoveryNode discoveryNod private static void validateAwarenessAttribute( final DecommissionAttribute decommissionAttribute, List awarenessAttributes, - Map> forcedAwarenessAttributes) { - if (awarenessAttributes == null || forcedAwarenessAttributes == null) { - throw new DecommissionFailedException(decommissionAttribute, "awareness attribute and forced awareness attribute not set to the cluster."); + Map> forcedAwarenessAttributes + ) { + String msg = null; + if (awarenessAttributes == null) { + msg = "awareness attribute not set to the cluster."; } - if (!awarenessAttributes.contains(decommissionAttribute.attributeName())) { - throw new DecommissionFailedException(decommissionAttribute, "invalid awareness attribute requested for decommissioning"); + else if (forcedAwarenessAttributes == null) { + msg = "forced awareness attribute not set to the cluster."; } - if (!forcedAwarenessAttributes.get(decommissionAttribute.attributeName()).contains(decommissionAttribute.attributeValue()) ) { - throw new DecommissionFailedException( - decommissionAttribute, - "invalid awareness attribute value requested for decommissioning. Set forced awareness values before to decommission" - ); + else if (!awarenessAttributes.contains(decommissionAttribute.attributeName())) { + msg = "invalid awareness attribute requested for decommissioning"; + } + else if (!forcedAwarenessAttributes.containsKey(decommissionAttribute.attributeName())) { + msg = "forced awareness attribute [" + forcedAwarenessAttributes.toString() + "] doesn't have the decommissioning attribute"; + } + else if (!forcedAwarenessAttributes.get(decommissionAttribute.attributeName()).contains(decommissionAttribute.attributeValue()) ) { + msg = "invalid awareness attribute value requested for decommissioning. Set forced awareness values before to decommission"; + } + + if (msg != null) { + throw new DecommissionFailedException(decommissionAttribute, msg); } } diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java index f2baea304344a..5f55387745ca0 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java @@ -19,6 +19,7 @@ import org.opensearch.cluster.ClusterStateObserver; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; import org.opensearch.cluster.coordination.CoordinationMetadata; +import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.node.DiscoveryNodeRole; @@ -146,6 +147,30 @@ public void testDecommissioningNotInitiatedForInvalidAttributeValue() { ); } + @SuppressWarnings("unchecked") + public void testDecommissioningNotInitiatedWhenAlreadyDecommissioned() { + DecommissionAttributeMetadata oldMetadata = new DecommissionAttributeMetadata( + new DecommissionAttribute("zone", "zone_1"), + DecommissionStatus.DECOMMISSION_IN_PROGRESS + ); + final ClusterState.Builder builder = builder(clusterService.state()); + setState( + clusterService, + builder.metadata( + Metadata.builder( + clusterService.state().metadata()).putCustom(DecommissionAttributeMetadata.TYPE, oldMetadata).build() + )); + ActionListener listener = mock(ActionListener.class); + DecommissionFailedException e = expectThrows(DecommissionFailedException.class, () -> { + decommissionService.initiateAttributeDecommissioning( + new DecommissionAttribute("zone", "zone_2"), listener, clusterService.state()); + }); + assertThat( + e.getMessage(), + Matchers.endsWith("one awareness attribute already decommissioned, recommission before triggering another decommission") + ); + } + private ClusterState addNodes(ClusterState clusterState, String zone, String... nodeIds) { DiscoveryNodes.Builder nodeBuilder = DiscoveryNodes.builder(clusterState.nodes()); org.opensearch.common.collect.List.of(nodeIds).forEach(nodeId -> nodeBuilder.add(newNode(nodeId, singletonMap("zone", zone)))); From f262b4f6a9d566e9839f17e6d1a4ac4a8d35f87a Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 30 Aug 2022 13:02:59 +0530 Subject: [PATCH 145/187] Fix decommission initiation Signed-off-by: Rishab Nahata --- .../opensearch/cluster/decommission/DecommissionService.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 5e86954cc18b2..8ffa388bf414f 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -127,8 +127,11 @@ public void initiateAttributeDecommissioning( final ActionListener listener, ClusterState state ) { + // validates if the correct awareness attributes and forced awareness attribute set to the cluster before initiating decommission action validateAwarenessAttribute(decommissionAttribute, awarenessAttributes, forcedAwarenessAttributes); + DecommissionAttributeMetadata decommissionAttributeMetadata = state.metadata().custom(DecommissionAttributeMetadata.TYPE); + // validates that there's no inflight decommissioning or already executed decommission in place ensureNoAwarenessAttributeDecommissioned(decommissionAttributeMetadata, decommissionAttribute); logger.info("initiating awareness attribute [{}] decommissioning", decommissionAttribute.toString()); @@ -168,6 +171,7 @@ private void excludeDecommissionedClusterManagerNodesFromVotingConfig(Decommissi && excludedNodesName.containsAll(clusterManagerNodesNameToBeDecommissioned))) { return; } + // send a transport request to exclude to-be-decommissioned cluster manager eligible nodes from voting config decommissionController.excludeDecommissionedNodesFromVotingConfig( clusterManagerNodesNameToBeDecommissioned, new ActionListener() { From 86628803e53410a1182232b992b3ec4a52f228b4 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 30 Aug 2022 14:17:59 +0530 Subject: [PATCH 146/187] Changes Signed-off-by: Rishab Nahata --- .../decommission/DecommissionService.java | 118 +++++++++--------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 8ffa388bf414f..16c77a758a433 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -214,43 +214,37 @@ private void registerDecommissionAttribute( new ClusterStateUpdateTask(Priority.URGENT) { @Override public ClusterState execute(ClusterState currentState) { - logger.info( - "registering decommission metadata for attribute [{}] with status as [{}]", - decommissionAttribute.toString(), - DecommissionStatus.DECOMMISSION_INIT - ); Metadata metadata = currentState.metadata(); Metadata.Builder mdBuilder = Metadata.builder(metadata); DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.custom(DecommissionAttributeMetadata.TYPE); ensureNoAwarenessAttributeDecommissioned(decommissionAttributeMetadata, decommissionAttribute); decommissionAttributeMetadata = new DecommissionAttributeMetadata(decommissionAttribute); mdBuilder.putCustom(DecommissionAttributeMetadata.TYPE, decommissionAttributeMetadata); + logger.info( + "registering decommission metadata for attribute [{}] with status as [{}]", + decommissionAttribute.toString(), + DecommissionStatus.DECOMMISSION_INIT + ); return ClusterState.builder(currentState).metadata(mdBuilder).build(); } @Override public void onFailure(String source, Exception e) { if (e instanceof DecommissionFailedException) { - logger.error( - () -> new ParameterizedMessage("failed to decommission attribute [{}]", decommissionAttribute.toString()), - e - ); + logger.error(() -> new ParameterizedMessage("failed to decommission attribute [{}]", decommissionAttribute.toString()), e); listener.onFailure(e); } else if (e instanceof NotClusterManagerException) { logger.debug( () -> new ParameterizedMessage( "cluster-manager updated while executing request for decommission attribute [{}]", decommissionAttribute.toString() - ), - e + ), e ); + // we don't want to send the failure response to the listener here as the request will be retried } else { - logger.error( - () -> new ParameterizedMessage( - "failed to initiate decommissioning for attribute [{}]", - decommissionAttribute.toString() - ), - e + logger.error(() -> new ParameterizedMessage( + "failed to initiate decommissioning for attribute [{}]", decommissionAttribute.toString() + ), e ); listener.onFailure(e); } @@ -270,30 +264,29 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS } private void initiateGracefulDecommission() { - // maybe create a supplier for status update listener? - ActionListener listener = new ActionListener<>() { - @Override - public void onResponse(Void unused) { - logger.info( - "updated decommission status to [{}], weighing away awareness attribute for graceful shutdown", - DecommissionStatus.DECOMMISSION_IN_PROGRESS - ); - failDecommissionedNodes(clusterService.getClusterApplierService().state()); - } - - @Override - public void onFailure(Exception e) { - logger.error( - () -> new ParameterizedMessage( - "failed to update decommission status to [{}], will not proceed with decommission", + decommissionController.updateMetadataWithDecommissionStatus( + DecommissionStatus.DECOMMISSION_IN_PROGRESS, + new ActionListener() { + @Override + public void onResponse(Void unused) { + logger.info( + "updated decommission status to [{}], weighing away awareness attribute for graceful shutdown", DecommissionStatus.DECOMMISSION_IN_PROGRESS - ), - e - ); + ); + // TODO - should trigger weigh away here and on successful weigh away -> fail the decommissioned nodes + failDecommissionedNodes(clusterService.getClusterApplierService().state()); + } + + @Override + public void onFailure(Exception e) { + logger.error(() -> new ParameterizedMessage( + "failed to update decommission status to [{}], will not proceed with decommission", + DecommissionStatus.DECOMMISSION_IN_PROGRESS + ), e + ); + } } - }; - decommissionController.updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_IN_PROGRESS, listener); - // TODO - code for graceful decommission + ); } private void failDecommissionedNodes(ClusterState state) { @@ -302,13 +295,31 @@ private void failDecommissionedNodes(ClusterState state) { : "unexpected status encountered while decommissioning nodes"; DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute(); - ActionListener statusUpdateListener = new ActionListener<>() { + // execute nodes decommissioning + decommissionController.handleNodesDecommissionRequest( + nodesWithDecommissionAttribute(state, decommissionAttribute, false), + "nodes-decommissioned", + TimeValue.timeValueSeconds(30L), // TODO - read timeout from request while integrating with API + new ActionListener() { + @Override + public void onResponse(Void unused) { + clearVotingConfigExclusionAndUpdateStatus(true); + } + + @Override + public void onFailure(Exception e) { + clearVotingConfigExclusionAndUpdateStatus(false); + } + } + ); + } + + private void clearVotingConfigExclusionAndUpdateStatus(boolean decommissionSuccessful) { + ActionListener statusUpdateListener = new ActionListener() { @Override public void onResponse(Void unused) { - logger.info( - "updated decommission status to [{}], decommissioning completed.", - DecommissionStatus.DECOMMISSION_SUCCESSFUL - ); + logger.info("successful updated decommission status with [{}]", + decommissionSuccessful ? DecommissionStatus.DECOMMISSION_SUCCESSFUL : DecommissionStatus.DECOMMISSION_FAILED); } @Override @@ -316,31 +327,20 @@ public void onFailure(Exception e) { logger.error("failed to update the decommission status"); } }; - - // execute nodes decommissioning and wait for it to complete - decommissionController.handleNodesDecommissionRequest( - nodesWithDecommissionAttribute(state, decommissionAttribute, false), - "nodes-decommissioned", - TimeValue.timeValueSeconds(30L), + decommissionController.clearVotingConfigExclusion( new ActionListener() { @Override public void onResponse(Void unused) { - decommissionController.updateMetadataWithDecommissionStatus( - DecommissionStatus.DECOMMISSION_SUCCESSFUL, - statusUpdateListener - ); + DecommissionStatus updateStatusWith = decommissionSuccessful? DecommissionStatus.DECOMMISSION_SUCCESSFUL : DecommissionStatus.DECOMMISSION_FAILED; + decommissionController.updateMetadataWithDecommissionStatus(updateStatusWith, statusUpdateListener); } @Override public void onFailure(Exception e) { - decommissionController.updateMetadataWithDecommissionStatus( - DecommissionStatus.DECOMMISSION_FAILED, - statusUpdateListener - ); + decommissionController.updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_FAILED, statusUpdateListener); } } ); -// decommissionController.clearVotingConfigExclusion(); } public Set nodesWithDecommissionAttribute( From 842e3ab9c4f5bfd539d1d55415e6d1a01a7554d1 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 30 Aug 2022 15:09:26 +0530 Subject: [PATCH 147/187] Move DecommissionAttributeMetadata to decommission package Signed-off-by: Rishab Nahata --- .../java/org/opensearch/cluster/ClusterModule.java | 2 +- .../cluster/coordination/JoinTaskExecutor.java | 6 +++--- .../DecommissionAttributeMetadata.java | 5 +++-- .../cluster/decommission/DecommissionController.java | 1 - .../cluster/decommission/DecommissionService.java | 1 - .../cluster/coordination/JoinTaskExecutorTests.java | 12 +++++------- .../decommission/DecommissionControllerTests.java | 1 - .../decommission/DecommissionServiceTests.java | 3 +-- ...ommissionAttributeMetadataSerializationTests.java | 1 + .../metadata/DecommissionAttributeMetadataTests.java | 1 + .../DecommissionAttributeMetadataXContentTests.java | 1 + 11 files changed, 16 insertions(+), 18 deletions(-) rename server/src/main/java/org/opensearch/cluster/{metadata => decommission}/DecommissionAttributeMetadata.java (98%) diff --git a/server/src/main/java/org/opensearch/cluster/ClusterModule.java b/server/src/main/java/org/opensearch/cluster/ClusterModule.java index 115b9bdf3d8d6..892e65e2ee5b4 100644 --- a/server/src/main/java/org/opensearch/cluster/ClusterModule.java +++ b/server/src/main/java/org/opensearch/cluster/ClusterModule.java @@ -35,10 +35,10 @@ import org.opensearch.cluster.action.index.MappingUpdatedAction; import org.opensearch.cluster.action.index.NodeMappingRefreshAction; import org.opensearch.cluster.action.shard.ShardStateAction; +import org.opensearch.cluster.decommission.DecommissionAttributeMetadata; import org.opensearch.cluster.metadata.ComponentTemplateMetadata; import org.opensearch.cluster.metadata.ComposableIndexTemplateMetadata; import org.opensearch.cluster.metadata.DataStreamMetadata; -import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; import org.opensearch.cluster.metadata.IndexGraveyard; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.metadata.Metadata; diff --git a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java index 7008474222aef..a104499109683 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java @@ -40,9 +40,9 @@ import org.opensearch.cluster.NotClusterManagerException; import org.opensearch.cluster.block.ClusterBlocks; import org.opensearch.cluster.decommission.DecommissionAttribute; +import org.opensearch.cluster.decommission.DecommissionAttributeMetadata; import org.opensearch.cluster.decommission.DecommissionStatus; import org.opensearch.cluster.decommission.NodeDecommissionedException; -import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; @@ -476,7 +476,7 @@ public static void ensureMajorVersionBarrier(Version joiningNodeVersion, Version } } - public static void ensureNodeNotDecommissioned(DiscoveryNode node, Metadata metadata) { + public static void ensureNodeCommissioned(DiscoveryNode node, Metadata metadata) { DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.custom(DecommissionAttributeMetadata.TYPE); if (decommissionAttributeMetadata != null) { DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute(); @@ -506,7 +506,7 @@ public static Collection> addBuiltInJoin validators.add((node, state) -> { ensureNodesCompatibility(node.getVersion(), state.getNodes()); ensureIndexCompatibility(node.getVersion(), state.getMetadata()); - ensureNodeNotDecommissioned(node, state.getMetadata()); + ensureNodeCommissioned(node, state.getMetadata()); }); validators.addAll(onJoinValidators); return Collections.unmodifiableCollection(validators); diff --git a/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttributeMetadata.java similarity index 98% rename from server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java rename to server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttributeMetadata.java index 2034ab34e25c3..72bdfbdca78d3 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadata.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttributeMetadata.java @@ -6,7 +6,7 @@ * compatible open source license. */ -package org.opensearch.cluster.metadata; +package org.opensearch.cluster.decommission; import org.opensearch.OpenSearchParseException; import org.opensearch.Version; @@ -14,6 +14,7 @@ import org.opensearch.cluster.NamedDiff; import org.opensearch.cluster.decommission.DecommissionAttribute; import org.opensearch.cluster.decommission.DecommissionStatus; +import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.metadata.Metadata.Custom; import org.opensearch.common.Strings; import org.opensearch.common.io.stream.StreamInput; @@ -162,7 +163,7 @@ public static DecommissionAttributeMetadata fromXContent(XContentParser parser) ); } decommissionAttribute = new DecommissionAttribute(fieldName, value); - token = parser.nextToken(); + parser.nextToken(); } else { throw new OpenSearchParseException("failed to parse attribute type [{}], unexpected type", attributeType); } diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java index d699fc51e819a..4f81ddf1e32ff 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java @@ -26,7 +26,6 @@ import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; import org.opensearch.cluster.coordination.NodeRemovalClusterStateTaskExecutor; -import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.routing.allocation.AllocationService; diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 16c77a758a433..70a3b18519e89 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -17,7 +17,6 @@ import org.opensearch.cluster.NotClusterManagerException; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; import org.opensearch.cluster.coordination.CoordinationMetadata; -import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.routing.allocation.AllocationService; diff --git a/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java b/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java index a3c3b9a9b9c7b..9e2d0cc3a7fc4 100644 --- a/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java +++ b/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java @@ -37,10 +37,9 @@ import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateTaskExecutor; import org.opensearch.cluster.decommission.DecommissionAttribute; -import org.opensearch.cluster.decommission.DecommissionFailedException; +import org.opensearch.cluster.decommission.DecommissionAttributeMetadata; import org.opensearch.cluster.decommission.DecommissionStatus; import org.opensearch.cluster.decommission.NodeDecommissionedException; -import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; @@ -58,7 +57,6 @@ import java.util.HashSet; import java.util.Map; -import static java.util.Collections.emptyMap; import static org.hamcrest.Matchers.is; import static org.opensearch.test.VersionUtils.allVersions; import static org.opensearch.test.VersionUtils.maxCompatibleVersion; @@ -231,7 +229,7 @@ public void testJoinClusterWithNoDecommission() { Metadata.Builder metaBuilder = Metadata.builder(); Metadata metadata = metaBuilder.build(); DiscoveryNode discoveryNode = newDiscoveryNode(Collections.singletonMap("zone", "zone-2")); - JoinTaskExecutor.ensureNodeNotDecommissioned(discoveryNode, metadata); + JoinTaskExecutor.ensureNodeCommissioned(discoveryNode, metadata); } public void testPreventJoinClusterWithDecommission() { @@ -252,7 +250,7 @@ public void testPreventJoinClusterWithDecommission() { DiscoveryNode discoveryNode = newDiscoveryNode(Collections.singletonMap("zone", "zone-1")); expectThrows( NodeDecommissionedException.class, - () -> JoinTaskExecutor.ensureNodeNotDecommissioned(discoveryNode, metadata) + () -> JoinTaskExecutor.ensureNodeCommissioned(discoveryNode, metadata) ); } @@ -269,7 +267,7 @@ public void testJoinClusterWithDifferentDecommission() { Metadata metadata = metaBuilder.build(); DiscoveryNode discoveryNode = newDiscoveryNode(Collections.singletonMap("zone", "zone-2")); - JoinTaskExecutor.ensureNodeNotDecommissioned(discoveryNode, metadata); + JoinTaskExecutor.ensureNodeCommissioned(discoveryNode, metadata); } public void testJoinClusterWithDecommissionFailedOrInitOrRecommission() { @@ -289,7 +287,7 @@ public void testJoinClusterWithDecommissionFailedOrInitOrRecommission() { Metadata metadata = metaBuilder.build(); DiscoveryNode discoveryNode = newDiscoveryNode(Collections.singletonMap("zone", "zone-1")); - JoinTaskExecutor.ensureNodeNotDecommissioned(discoveryNode, metadata); + JoinTaskExecutor.ensureNodeCommissioned(discoveryNode, metadata); } private DiscoveryNode newDiscoveryNode(Map attributes) { diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java index 0f7f76fc5eeec..ee97f1768cd94 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java @@ -26,7 +26,6 @@ import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; import org.opensearch.cluster.coordination.CoordinationMetadata; -import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java index 5f55387745ca0..d93cd5ea51978 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java @@ -19,7 +19,6 @@ import org.opensearch.cluster.ClusterStateObserver; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; import org.opensearch.cluster.coordination.CoordinationMetadata; -import org.opensearch.cluster.metadata.DecommissionAttributeMetadata; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.node.DiscoveryNodeRole; @@ -160,7 +159,7 @@ public void testDecommissioningNotInitiatedWhenAlreadyDecommissioned() { Metadata.builder( clusterService.state().metadata()).putCustom(DecommissionAttributeMetadata.TYPE, oldMetadata).build() )); - ActionListener listener = mock(ActionListener.class); + ActionListener listener = mock(ActionListener.class); DecommissionFailedException e = expectThrows(DecommissionFailedException.class, () -> { decommissionService.initiateAttributeDecommissioning( new DecommissionAttribute("zone", "zone_2"), listener, clusterService.state()); diff --git a/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataSerializationTests.java b/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataSerializationTests.java index d81c05b8e8da0..5423c2ed672a3 100644 --- a/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataSerializationTests.java +++ b/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataSerializationTests.java @@ -11,6 +11,7 @@ import org.opensearch.cluster.ClusterModule; import org.opensearch.cluster.Diff; import org.opensearch.cluster.decommission.DecommissionAttribute; +import org.opensearch.cluster.decommission.DecommissionAttributeMetadata; import org.opensearch.cluster.decommission.DecommissionStatus; import org.opensearch.common.io.stream.NamedWriteableRegistry; import org.opensearch.common.io.stream.Writeable; diff --git a/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataTests.java b/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataTests.java index bff57daef6109..746d4565b0db3 100644 --- a/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataTests.java +++ b/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataTests.java @@ -9,6 +9,7 @@ package org.opensearch.cluster.metadata; import org.opensearch.cluster.decommission.DecommissionAttribute; +import org.opensearch.cluster.decommission.DecommissionAttributeMetadata; import org.opensearch.cluster.decommission.DecommissionStatus; import org.opensearch.common.io.stream.NamedWriteableRegistry; import org.opensearch.test.AbstractNamedWriteableTestCase; diff --git a/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataXContentTests.java b/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataXContentTests.java index c632839acd4ca..030946f4510a1 100644 --- a/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataXContentTests.java +++ b/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataXContentTests.java @@ -9,6 +9,7 @@ package org.opensearch.cluster.metadata; import org.opensearch.cluster.decommission.DecommissionAttribute; +import org.opensearch.cluster.decommission.DecommissionAttributeMetadata; import org.opensearch.cluster.decommission.DecommissionStatus; import org.opensearch.common.xcontent.XContentParser; import org.opensearch.test.AbstractXContentTestCase; From 86fc238c63b3fe68376e23fd2a9a8a356770d0f8 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 30 Aug 2022 15:38:00 +0530 Subject: [PATCH 148/187] Update exception name Signed-off-by: Rishab Nahata --- .../java/org/opensearch/OpenSearchException.java | 5 +++-- .../cluster/decommission/DecommissionService.java | 12 +++--------- ...ion.java => DecommissioningFailedException.java} | 8 ++++---- .../org/opensearch/ExceptionSerializationTests.java | 4 ++-- .../decommission/DecommissionServiceTests.java | 13 +++---------- 5 files changed, 15 insertions(+), 27 deletions(-) rename server/src/main/java/org/opensearch/cluster/decommission/{DecommissionFailedException.java => DecommissioningFailedException.java} (78%) diff --git a/server/src/main/java/org/opensearch/OpenSearchException.java b/server/src/main/java/org/opensearch/OpenSearchException.java index 5ae83c9df70d3..7799aef7fab38 100644 --- a/server/src/main/java/org/opensearch/OpenSearchException.java +++ b/server/src/main/java/org/opensearch/OpenSearchException.java @@ -34,6 +34,7 @@ import org.opensearch.action.support.replication.ReplicationOperation; import org.opensearch.cluster.action.shard.ShardStateAction; +import org.opensearch.cluster.decommission.DecommissioningFailedException; import org.opensearch.common.CheckedFunction; import org.opensearch.common.Nullable; import org.opensearch.common.ParseField; @@ -1611,8 +1612,8 @@ private enum OpenSearchExceptionHandle { V_3_0_0 ), DECOMMISSION_FAILED_EXCEPTION( - org.opensearch.cluster.decommission.DecommissionFailedException.class, - org.opensearch.cluster.decommission.DecommissionFailedException::new, + org.opensearch.cluster.decommission.DecommissioningFailedException.class, + org.opensearch.cluster.decommission.DecommissioningFailedException::new, 163, V_2_3_0 ), diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 70a3b18519e89..eac713f4961b4 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -16,25 +16,19 @@ import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.NotClusterManagerException; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; -import org.opensearch.cluster.coordination.CoordinationMetadata; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.routing.allocation.AllocationService; -import org.opensearch.cluster.routing.allocation.decider.AwarenessAllocationDecider; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Priority; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; import org.opensearch.threadpool.ThreadPool; -import org.opensearch.transport.TransportException; -import org.opensearch.transport.TransportResponseHandler; import org.opensearch.transport.TransportService; import org.opensearch.cluster.coordination.CoordinationMetadata.VotingConfigExclusion; -import java.io.IOException; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -229,7 +223,7 @@ public ClusterState execute(ClusterState currentState) { @Override public void onFailure(String source, Exception e) { - if (e instanceof DecommissionFailedException) { + if (e instanceof DecommissioningFailedException) { logger.error(() -> new ParameterizedMessage("failed to decommission attribute [{}]", decommissionAttribute.toString()), e); listener.onFailure(e); } else if (e instanceof NotClusterManagerException) { @@ -391,7 +385,7 @@ else if (!forcedAwarenessAttributes.get(decommissionAttribute.attributeName()).c } if (msg != null) { - throw new DecommissionFailedException(decommissionAttribute, msg); + throw new DecommissioningFailedException(decommissionAttribute, msg); } } @@ -402,7 +396,7 @@ private static void ensureNoAwarenessAttributeDecommissioned( // If the previous decommission request failed, we will allow the request to pass this check if (decommissionAttributeMetadata != null && !decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_FAILED)) { - throw new DecommissionFailedException( + throw new DecommissioningFailedException( decommissionAttribute, "one awareness attribute already decommissioned, recommission before triggering another decommission" ); diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionFailedException.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissioningFailedException.java similarity index 78% rename from server/src/main/java/org/opensearch/cluster/decommission/DecommissionFailedException.java rename to server/src/main/java/org/opensearch/cluster/decommission/DecommissioningFailedException.java index 3ba121dd90cee..fe1b9368ac712 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionFailedException.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissioningFailedException.java @@ -20,20 +20,20 @@ * @opensearch.internal */ -public class DecommissionFailedException extends OpenSearchException { +public class DecommissioningFailedException extends OpenSearchException { private final DecommissionAttribute decommissionAttribute; - public DecommissionFailedException(DecommissionAttribute decommissionAttribute, String msg) { + public DecommissioningFailedException(DecommissionAttribute decommissionAttribute, String msg) { this(decommissionAttribute, msg, null); } - public DecommissionFailedException(DecommissionAttribute decommissionAttribute, String msg, Throwable cause) { + public DecommissioningFailedException(DecommissionAttribute decommissionAttribute, String msg, Throwable cause) { super("[" + (decommissionAttribute == null ? "_na" : decommissionAttribute.toString()) + "] " + msg, cause); this.decommissionAttribute = decommissionAttribute; } - public DecommissionFailedException(StreamInput in) throws IOException { + public DecommissioningFailedException(StreamInput in) throws IOException { super(in); decommissionAttribute = new DecommissionAttribute(in); } diff --git a/server/src/test/java/org/opensearch/ExceptionSerializationTests.java b/server/src/test/java/org/opensearch/ExceptionSerializationTests.java index 6516cc80c7929..ff2bb77531486 100644 --- a/server/src/test/java/org/opensearch/ExceptionSerializationTests.java +++ b/server/src/test/java/org/opensearch/ExceptionSerializationTests.java @@ -49,7 +49,7 @@ import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.coordination.CoordinationStateRejectedException; import org.opensearch.cluster.coordination.NoClusterManagerBlockService; -import org.opensearch.cluster.decommission.DecommissionFailedException; +import org.opensearch.cluster.decommission.DecommissioningFailedException; import org.opensearch.cluster.decommission.NodeDecommissionedException; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.routing.IllegalShardRoutingStateException; @@ -862,7 +862,7 @@ public void testIds() { ids.put(160, NoSeedNodeLeftException.class); ids.put(161, ReplicationFailedException.class); ids.put(162, PrimaryShardClosedException.class); - ids.put(163, DecommissionFailedException.class); + ids.put(163, DecommissioningFailedException.class); ids.put(164, NodeDecommissionedException.class); Map, Integer> reverse = new HashMap<>(); diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java index d93cd5ea51978..13d036b2952f7 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java @@ -13,10 +13,8 @@ import org.junit.Before; import org.opensearch.Version; import org.opensearch.action.ActionListener; -import org.opensearch.action.search.CreatePitController; import org.opensearch.cluster.ClusterName; import org.opensearch.cluster.ClusterState; -import org.opensearch.cluster.ClusterStateObserver; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; import org.opensearch.cluster.coordination.CoordinationMetadata; import org.opensearch.cluster.metadata.Metadata; @@ -25,8 +23,6 @@ import org.opensearch.cluster.node.DiscoveryNodes; import org.opensearch.cluster.routing.allocation.AllocationService; import org.opensearch.cluster.routing.allocation.decider.AwarenessAllocationDecider; -import org.opensearch.cluster.routing.allocation.decider.ClusterRebalanceAllocationDecider; -import org.opensearch.cluster.routing.allocation.decider.ThrottlingAllocationDecider; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Settings; @@ -44,12 +40,9 @@ import static java.util.Collections.emptySet; import static java.util.Collections.singletonMap; -import static org.mockito.ArgumentMatchers.startsWith; import static org.mockito.Mockito.mock; import static org.opensearch.cluster.ClusterState.builder; import static org.opensearch.cluster.OpenSearchAllocationTestCase.createAllocationService; -import static org.opensearch.cluster.coordination.NoClusterManagerBlockService.NO_CLUSTER_MANAGER_BLOCK_SETTING; -import static org.opensearch.cluster.routing.allocation.decider.AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING; import static org.opensearch.test.ClusterServiceUtils.createClusterService; import static org.opensearch.test.ClusterServiceUtils.setState; @@ -125,7 +118,7 @@ public void shutdownThreadPoolAndClusterService() { public void testDecommissioningNotInitiatedForInvalidAttributeName() { DecommissionAttribute decommissionAttribute = new DecommissionAttribute("rack", "rack-a"); ActionListener listener = mock(ActionListener.class); - DecommissionFailedException e = expectThrows(DecommissionFailedException.class, () -> { + DecommissioningFailedException e = expectThrows(DecommissioningFailedException.class, () -> { decommissionService.initiateAttributeDecommissioning( decommissionAttribute, listener, clusterService.state()); }); @@ -136,7 +129,7 @@ public void testDecommissioningNotInitiatedForInvalidAttributeName() { public void testDecommissioningNotInitiatedForInvalidAttributeValue() { DecommissionAttribute decommissionAttribute = new DecommissionAttribute("zone", "random"); ActionListener listener = mock(ActionListener.class); - DecommissionFailedException e = expectThrows(DecommissionFailedException.class, () -> { + DecommissioningFailedException e = expectThrows(DecommissioningFailedException.class, () -> { decommissionService.initiateAttributeDecommissioning( decommissionAttribute, listener, clusterService.state()); }); @@ -160,7 +153,7 @@ public void testDecommissioningNotInitiatedWhenAlreadyDecommissioned() { clusterService.state().metadata()).putCustom(DecommissionAttributeMetadata.TYPE, oldMetadata).build() )); ActionListener listener = mock(ActionListener.class); - DecommissionFailedException e = expectThrows(DecommissionFailedException.class, () -> { + DecommissioningFailedException e = expectThrows(DecommissioningFailedException.class, () -> { decommissionService.initiateAttributeDecommissioning( new DecommissionAttribute("zone", "zone_2"), listener, clusterService.state()); }); From 94b2916c3260ae7999e5e747a9dac218c7a28629 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 30 Aug 2022 15:46:13 +0530 Subject: [PATCH 149/187] Fix spotless and precommit checks Signed-off-by: Rishab Nahata --- .../coordination/JoinTaskExecutor.java | 7 +- .../DecommissionAttributeMetadata.java | 2 - .../decommission/DecommissionController.java | 20 +--- .../decommission/DecommissionService.java | 108 ++++++++++-------- .../coordination/JoinTaskExecutorTests.java | 5 +- .../DecommissionControllerTests.java | 92 ++++++--------- .../DecommissionServiceTests.java | 62 +++++----- ...onAttributeMetadataSerializationTests.java | 7 +- 8 files changed, 135 insertions(+), 168 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java index a104499109683..ebf37e21bbfd6 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java @@ -484,11 +484,8 @@ public static void ensureNodeCommissioned(DiscoveryNode node, Metadata metadata) if (decommissionAttribute != null && status != null) { // We will let the node join the cluster if the current status is not IN_PROGRESS or SUCCESSFUL if (node.getAttributes().get(decommissionAttribute.attributeName()).equals(decommissionAttribute.attributeValue()) - && ( - status.equals(DecommissionStatus.DECOMMISSION_IN_PROGRESS) - || status.equals(DecommissionStatus.DECOMMISSION_SUCCESSFUL - ) - )) { + && (status.equals(DecommissionStatus.DECOMMISSION_IN_PROGRESS) + || status.equals(DecommissionStatus.DECOMMISSION_SUCCESSFUL))) { throw new NodeDecommissionedException( "node [{}] has decommissioned attribute [{}].", node.toString(), diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttributeMetadata.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttributeMetadata.java index 72bdfbdca78d3..0924a181fb458 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttributeMetadata.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttributeMetadata.java @@ -12,8 +12,6 @@ import org.opensearch.Version; import org.opensearch.cluster.AbstractNamedDiffable; import org.opensearch.cluster.NamedDiff; -import org.opensearch.cluster.decommission.DecommissionAttribute; -import org.opensearch.cluster.decommission.DecommissionStatus; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.metadata.Metadata.Custom; import org.opensearch.common.Strings; diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java index 4f81ddf1e32ff..1799479efe4cc 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java @@ -24,7 +24,6 @@ import org.opensearch.cluster.ClusterStateTaskConfig; import org.opensearch.cluster.ClusterStateTaskListener; import org.opensearch.cluster.ClusterStateUpdateTask; -import org.opensearch.cluster.ack.ClusterStateUpdateResponse; import org.opensearch.cluster.coordination.NodeRemovalClusterStateTaskExecutor; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; @@ -34,7 +33,6 @@ import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.unit.TimeValue; import org.opensearch.threadpool.ThreadPool; -import org.opensearch.transport.Transport; import org.opensearch.transport.TransportException; import org.opensearch.transport.TransportResponseHandler; import org.opensearch.transport.TransportService; @@ -42,7 +40,6 @@ import java.io.IOException; import java.util.Iterator; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Predicate; @@ -78,7 +75,7 @@ public void excludeDecommissionedNodesFromVotingConfig(Set nodes, Action transportService.sendRequest( transportService.getLocalNode(), AddVotingConfigExclusionsAction.NAME, - new AddVotingConfigExclusionsRequest(nodes.stream().toArray(String[] :: new)), + new AddVotingConfigExclusionsRequest(nodes.stream().toArray(String[]::new)), new TransportResponseHandler() { @Override public void handleResponse(AddVotingConfigExclusionsResponse response) { @@ -166,12 +163,7 @@ public void handleNodesDecommissionRequest( return true; }; - final ClusterStateObserver observer = new ClusterStateObserver( - clusterService, - timeout, - logger, - threadPool.getThreadContext() - ); + final ClusterStateObserver observer = new ClusterStateObserver(clusterService, timeout, logger, threadPool.getThreadContext()); observer.waitForNextChange(new ClusterStateObserver.Listener() { @Override @@ -198,10 +190,7 @@ public void onTimeout(TimeValue timeout) { }, allDecommissionedNodesRemovedPredicate); } - public void updateMetadataWithDecommissionStatus( - DecommissionStatus decommissionStatus, - ActionListener listener - ) { + public void updateMetadataWithDecommissionStatus(DecommissionStatus decommissionStatus, ActionListener listener) { clusterService.submitStateUpdateTask(decommissionStatus.status(), new ClusterStateUpdateTask(Priority.URGENT) { @Override public ClusterState execute(ClusterState currentState) { @@ -222,7 +211,8 @@ public void onFailure(String source, Exception e) { @Override public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { - DecommissionAttributeMetadata decommissionAttributeMetadata = newState.metadata().custom(DecommissionAttributeMetadata.TYPE); + DecommissionAttributeMetadata decommissionAttributeMetadata = newState.metadata() + .custom(DecommissionAttributeMetadata.TYPE); assert decommissionAttributeMetadata.status().equals(decommissionStatus); listener.onResponse(null); } diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index eac713f4961b4..89ddabb9fa19e 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -80,17 +80,9 @@ public DecommissionService( this.clusterService = clusterService; this.transportService = transportService; this.threadPool = threadPool; - this.decommissionController = new DecommissionController( - clusterService, - transportService, - allocationService, - threadPool - ); + this.decommissionController = new DecommissionController(clusterService, transportService, allocationService, threadPool); this.awarenessAttributes = CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING.get(settings); - clusterSettings.addSettingsUpdateConsumer( - CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING, - this::setAwarenessAttributes - ); + clusterSettings.addSettingsUpdateConsumer(CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING, this::setAwarenessAttributes); setForcedAwarenessAttributes(CLUSTER_ROUTING_ALLOCATION_AWARENESS_FORCE_GROUP_SETTING.get(settings)); clusterSettings.addSettingsUpdateConsumer( @@ -120,7 +112,8 @@ public void initiateAttributeDecommissioning( final ActionListener listener, ClusterState state ) { - // validates if the correct awareness attributes and forced awareness attribute set to the cluster before initiating decommission action + // validates if the correct awareness attributes and forced awareness attribute set to the cluster before initiating decommission + // action validateAwarenessAttribute(decommissionAttribute, awarenessAttributes, forcedAwarenessAttributes); DecommissionAttributeMetadata decommissionAttributeMetadata = state.metadata().custom(DecommissionAttributeMetadata.TYPE); @@ -135,7 +128,7 @@ public void initiateAttributeDecommissioning( // explicitly throwing NotClusterManagerException as we can certainly say the local cluster manager node will // be abdicated and soon will no longer be cluster manager. - if(transportService.getLocalNode().isClusterManagerNode() + if (transportService.getLocalNode().isClusterManagerNode() && !nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute)) { registerDecommissionAttribute(decommissionAttribute, listener); } else { @@ -149,19 +142,26 @@ public void initiateAttributeDecommissioning( private void excludeDecommissionedClusterManagerNodesFromVotingConfig(DecommissionAttribute decommissionAttribute) { Set clusterManagerNodesToBeDecommissioned = nodesWithDecommissionAttribute( - clusterService.state(), decommissionAttribute, true + clusterService.state(), + decommissionAttribute, + true ); Set clusterManagerNodesNameToBeDecommissioned = clusterManagerNodesToBeDecommissioned.stream() .map(DiscoveryNode::getName) .collect(Collectors.toSet()); - Set currentVotingConfigExclusions = clusterService.getClusterApplierService().state().coordinationMetadata().getVotingConfigExclusions(); - Set excludedNodesName = currentVotingConfigExclusions.stream().map(VotingConfigExclusion::getNodeName).collect(Collectors.toSet()); + Set currentVotingConfigExclusions = clusterService.getClusterApplierService() + .state() + .coordinationMetadata() + .getVotingConfigExclusions(); + Set excludedNodesName = currentVotingConfigExclusions.stream() + .map(VotingConfigExclusion::getNodeName) + .collect(Collectors.toSet()); // check if the to-be-excluded nodes are excluded. If yes, we don't need to exclude them again if (clusterManagerNodesNameToBeDecommissioned.size() == 0 || (clusterManagerNodesNameToBeDecommissioned.size() == excludedNodesName.size() - && excludedNodesName.containsAll(clusterManagerNodesNameToBeDecommissioned))) { + && excludedNodesName.containsAll(clusterManagerNodesNameToBeDecommissioned))) { return; } // send a transport request to exclude to-be-decommissioned cluster manager eligible nodes from voting config @@ -171,8 +171,8 @@ private void excludeDecommissionedClusterManagerNodesFromVotingConfig(Decommissi @Override public void onResponse(Void unused) { logger.info( - "successfully removed decommissioned cluster manager eligible nodes [{}] from voting config " - , clusterManagerNodesToBeDecommissioned.toString() + "successfully removed decommissioned cluster manager eligible nodes [{}] from voting config ", + clusterManagerNodesToBeDecommissioned.toString() ); } @@ -224,20 +224,27 @@ public ClusterState execute(ClusterState currentState) { @Override public void onFailure(String source, Exception e) { if (e instanceof DecommissioningFailedException) { - logger.error(() -> new ParameterizedMessage("failed to decommission attribute [{}]", decommissionAttribute.toString()), e); + logger.error( + () -> new ParameterizedMessage("failed to decommission attribute [{}]", decommissionAttribute.toString()), + e + ); listener.onFailure(e); } else if (e instanceof NotClusterManagerException) { logger.debug( () -> new ParameterizedMessage( "cluster-manager updated while executing request for decommission attribute [{}]", decommissionAttribute.toString() - ), e + ), + e ); // we don't want to send the failure response to the listener here as the request will be retried } else { - logger.error(() -> new ParameterizedMessage( - "failed to initiate decommissioning for attribute [{}]", decommissionAttribute.toString() - ), e + logger.error( + () -> new ParameterizedMessage( + "failed to initiate decommissioning for attribute [{}]", + decommissionAttribute.toString() + ), + e ); listener.onFailure(e); } @@ -272,10 +279,12 @@ public void onResponse(Void unused) { @Override public void onFailure(Exception e) { - logger.error(() -> new ParameterizedMessage( + logger.error( + () -> new ParameterizedMessage( "failed to update decommission status to [{}], will not proceed with decommission", DecommissionStatus.DECOMMISSION_IN_PROGRESS - ), e + ), + e ); } } @@ -311,8 +320,10 @@ private void clearVotingConfigExclusionAndUpdateStatus(boolean decommissionSucce ActionListener statusUpdateListener = new ActionListener() { @Override public void onResponse(Void unused) { - logger.info("successful updated decommission status with [{}]", - decommissionSuccessful ? DecommissionStatus.DECOMMISSION_SUCCESSFUL : DecommissionStatus.DECOMMISSION_FAILED); + logger.info( + "successful updated decommission status with [{}]", + decommissionSuccessful ? DecommissionStatus.DECOMMISSION_SUCCESSFUL : DecommissionStatus.DECOMMISSION_FAILED + ); } @Override @@ -320,20 +331,20 @@ public void onFailure(Exception e) { logger.error("failed to update the decommission status"); } }; - decommissionController.clearVotingConfigExclusion( - new ActionListener() { - @Override - public void onResponse(Void unused) { - DecommissionStatus updateStatusWith = decommissionSuccessful? DecommissionStatus.DECOMMISSION_SUCCESSFUL : DecommissionStatus.DECOMMISSION_FAILED; - decommissionController.updateMetadataWithDecommissionStatus(updateStatusWith, statusUpdateListener); - } + decommissionController.clearVotingConfigExclusion(new ActionListener() { + @Override + public void onResponse(Void unused) { + DecommissionStatus updateStatusWith = decommissionSuccessful + ? DecommissionStatus.DECOMMISSION_SUCCESSFUL + : DecommissionStatus.DECOMMISSION_FAILED; + decommissionController.updateMetadataWithDecommissionStatus(updateStatusWith, statusUpdateListener); + } - @Override - public void onFailure(Exception e) { - decommissionController.updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_FAILED, statusUpdateListener); - } + @Override + public void onFailure(Exception e) { + decommissionController.updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_FAILED, statusUpdateListener); } - ); + }); } public Set nodesWithDecommissionAttribute( @@ -346,8 +357,9 @@ public Set nodesWithDecommissionAttribute( discoveryNode, decommissionAttribute ); - Iterator nodesIter = onlyClusterManagerNodes? clusterState.nodes().getClusterManagerNodes().valuesIt() : - clusterState.nodes().getNodes().valuesIt(); + Iterator nodesIter = onlyClusterManagerNodes + ? clusterState.nodes().getClusterManagerNodes().valuesIt() + : clusterState.nodes().getNodes().valuesIt(); while (nodesIter.hasNext()) { final DiscoveryNode node = nodesIter.next(); @@ -369,18 +381,14 @@ private static void validateAwarenessAttribute( ) { String msg = null; if (awarenessAttributes == null) { - msg = "awareness attribute not set to the cluster."; - } - else if (forcedAwarenessAttributes == null) { + msg = "awareness attribute not set to the cluster."; + } else if (forcedAwarenessAttributes == null) { msg = "forced awareness attribute not set to the cluster."; - } - else if (!awarenessAttributes.contains(decommissionAttribute.attributeName())) { + } else if (!awarenessAttributes.contains(decommissionAttribute.attributeName())) { msg = "invalid awareness attribute requested for decommissioning"; - } - else if (!forcedAwarenessAttributes.containsKey(decommissionAttribute.attributeName())) { + } else if (!forcedAwarenessAttributes.containsKey(decommissionAttribute.attributeName())) { msg = "forced awareness attribute [" + forcedAwarenessAttributes.toString() + "] doesn't have the decommissioning attribute"; - } - else if (!forcedAwarenessAttributes.get(decommissionAttribute.attributeName()).contains(decommissionAttribute.attributeValue()) ) { + } else if (!forcedAwarenessAttributes.get(decommissionAttribute.attributeName()).contains(decommissionAttribute.attributeValue())) { msg = "invalid awareness attribute value requested for decommissioning. Set forced awareness values before to decommission"; } diff --git a/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java b/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java index 9e2d0cc3a7fc4..734f112bdce3d 100644 --- a/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java +++ b/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java @@ -248,10 +248,7 @@ public void testPreventJoinClusterWithDecommission() { Metadata metadata = metaBuilder.build(); DiscoveryNode discoveryNode = newDiscoveryNode(Collections.singletonMap("zone", "zone-1")); - expectThrows( - NodeDecommissionedException.class, - () -> JoinTaskExecutor.ensureNodeCommissioned(discoveryNode, metadata) - ); + expectThrows(NodeDecommissionedException.class, () -> JoinTaskExecutor.ensureNodeCommissioned(discoveryNode, metadata)); } public void testJoinClusterWithDifferentDecommission() { diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java index ee97f1768cd94..e5d7ec60c0e23 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java @@ -9,22 +9,15 @@ package org.opensearch.cluster.decommission; import org.junit.After; -import org.junit.AfterClass; import org.junit.Before; -import org.junit.BeforeClass; import org.opensearch.OpenSearchTimeoutException; import org.opensearch.Version; import org.opensearch.action.ActionListener; import org.opensearch.action.admin.cluster.configuration.TransportAddVotingConfigExclusionsAction; -import org.opensearch.action.admin.cluster.configuration.TransportAddVotingConfigExclusionsActionTests; import org.opensearch.action.admin.cluster.configuration.TransportClearVotingConfigExclusionsAction; import org.opensearch.action.support.ActionFilters; import org.opensearch.cluster.ClusterName; import org.opensearch.cluster.ClusterState; -import org.opensearch.cluster.coordination.CoordinationMetadata.VotingConfigExclusion; -import org.opensearch.cluster.ClusterStateObserver; -import org.opensearch.cluster.ClusterStateUpdateTask; -import org.opensearch.cluster.ack.ClusterStateUpdateResponse; import org.opensearch.cluster.coordination.CoordinationMetadata; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.metadata.Metadata; @@ -33,7 +26,6 @@ import org.opensearch.cluster.node.DiscoveryNodes; import org.opensearch.cluster.routing.allocation.AllocationService; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.collect.ImmutableOpenMap; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; @@ -44,11 +36,9 @@ import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; @@ -56,21 +46,11 @@ import java.util.stream.Collectors; import java.util.stream.StreamSupport; -import static java.util.Collections.emptyMap; import static java.util.Collections.emptySet; import static java.util.Collections.singletonMap; -import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.sameInstance; import static org.hamcrest.Matchers.startsWith; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.opensearch.action.admin.cluster.configuration.TransportAddVotingConfigExclusionsAction.MAXIMUM_VOTING_CONFIG_EXCLUSIONS_SETTING; import static org.opensearch.cluster.ClusterState.builder; import static org.opensearch.cluster.OpenSearchAllocationTestCase.createAllocationService; import static org.opensearch.test.ClusterServiceUtils.createClusterService; @@ -105,10 +85,7 @@ public void setTransportServiceAndDefaultClusterState() { clusterState = setLocalNodeAsClusterManagerNode(clusterState, "node1"); clusterState = setThreeNodesInVotingConfig(clusterState); final ClusterState.Builder builder = builder(clusterState); - setState( - clusterService, - builder - ); + setState(clusterService, builder); final MockTransport transport = new MockTransport(); transportService = transport.createTransportService( Settings.EMPTY, @@ -155,20 +132,17 @@ public void shutdownThreadPoolAndClusterService() { public void testAddNodesToVotingConfigExclusion() throws InterruptedException { final CountDownLatch countDownLatch = new CountDownLatch(1); Set nodesToRemoveFromVotingConfig = Collections.singleton(randomFrom("node1", "node6", "node11")); - decommissionController.excludeDecommissionedNodesFromVotingConfig( - nodesToRemoveFromVotingConfig, - new ActionListener() { - @Override - public void onResponse(Void unused) { - countDownLatch.countDown(); - } + decommissionController.excludeDecommissionedNodesFromVotingConfig(nodesToRemoveFromVotingConfig, new ActionListener() { + @Override + public void onResponse(Void unused) { + countDownLatch.countDown(); + } - @Override - public void onFailure(Exception e) { - fail("unexpected failure occurred while removing node from voting config " + e); - } + @Override + public void onFailure(Exception e) { + fail("unexpected failure occurred while removing node from voting config " + e); } - ); + }); assertTrue(countDownLatch.await(30, TimeUnit.SECONDS)); clusterService.getClusterApplierService().state().getVotingConfigExclusions().forEach(vce -> { assertTrue(nodesToRemoveFromVotingConfig.contains(vce.getNodeName())); @@ -186,14 +160,13 @@ public void onResponse(Void unused) { @Override public void onFailure(Exception e) { - fail("unexpected failure occurred while clearing voting config exclusion" + e); + fail("unexpected failure occurred while clearing voting config exclusion" + e); } }); assertTrue(countDownLatch.await(30, TimeUnit.SECONDS)); assertThat(clusterService.getClusterApplierService().state().getVotingConfigExclusions(), empty()); } - public void testNodesRemovedForDecommissionRequestSuccessfulResponse() throws InterruptedException { final CountDownLatch countDownLatch = new CountDownLatch(1); Set nodesToBeRemoved = new HashSet<>(); @@ -222,9 +195,8 @@ public void onFailure(Exception e) { assertTrue(countDownLatch.await(30, TimeUnit.SECONDS)); // test all 5 nodes removed and cluster has 10 nodes - Set nodes = StreamSupport.stream( - clusterService.getClusterApplierService().state().nodes().spliterator(), false - ).collect(Collectors.toSet()); + Set nodes = StreamSupport.stream(clusterService.getClusterApplierService().state().nodes().spliterator(), false) + .collect(Collectors.toSet()); assertEquals(nodes.size(), 10); // test no nodes part of zone-3 for (DiscoveryNode node : nodes) { @@ -274,20 +246,17 @@ public void testSuccessfulDecommissionStatusMetadataUpdate() throws InterruptedE state = ClusterState.builder(state).metadata(mdBuilder).build(); setState(clusterService, state); - decommissionController.updateMetadataWithDecommissionStatus( - DecommissionStatus.DECOMMISSION_SUCCESSFUL, - new ActionListener() { - @Override - public void onResponse(Void unused) { - countDownLatch.countDown(); - } + decommissionController.updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_SUCCESSFUL, new ActionListener() { + @Override + public void onResponse(Void unused) { + countDownLatch.countDown(); + } - @Override - public void onFailure(Exception e) { - fail("decommission status update failed"); - } + @Override + public void onFailure(Exception e) { + fail("decommission status update failed"); } - ); + }); assertTrue(countDownLatch.await(30, TimeUnit.SECONDS)); ClusterState newState = clusterService.getClusterApplierService().state(); DecommissionAttributeMetadata decommissionAttributeMetadata = newState.metadata().custom(DecommissionAttributeMetadata.TYPE); @@ -311,15 +280,18 @@ private ClusterState setLocalNodeAsClusterManagerNode(ClusterState clusterState, private ClusterState setThreeNodesInVotingConfig(ClusterState clusterState) { final CoordinationMetadata.VotingConfiguration votingConfiguration = CoordinationMetadata.VotingConfiguration.of( - clusterState.nodes().get("node1"), clusterState.nodes().get("node6"), clusterState.nodes().get("node11") + clusterState.nodes().get("node1"), + clusterState.nodes().get("node6"), + clusterState.nodes().get("node11") ); - Metadata.Builder builder = Metadata.builder().coordinationMetadata( - CoordinationMetadata.builder() - .lastAcceptedConfiguration(votingConfiguration) - .lastCommittedConfiguration(votingConfiguration) - .build() - ); + Metadata.Builder builder = Metadata.builder() + .coordinationMetadata( + CoordinationMetadata.builder() + .lastAcceptedConfiguration(votingConfiguration) + .lastCommittedConfiguration(votingConfiguration) + .build() + ); clusterState = ClusterState.builder(clusterState).metadata(builder).build(); return clusterState; } diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java index 13d036b2952f7..04bb876761113 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java @@ -76,10 +76,7 @@ public void setUpService() { clusterState = setLocalNodeAsClusterManagerNode(clusterState, "node1"); clusterState = setThreeNodesInVotingConfig(clusterState); final ClusterState.Builder builder = builder(clusterState); - setState( - clusterService, - builder - ); + setState(clusterService, builder); final MockTransport transport = new MockTransport(); transportService = transport.createTransportService( Settings.EMPTY, @@ -118,10 +115,10 @@ public void shutdownThreadPoolAndClusterService() { public void testDecommissioningNotInitiatedForInvalidAttributeName() { DecommissionAttribute decommissionAttribute = new DecommissionAttribute("rack", "rack-a"); ActionListener listener = mock(ActionListener.class); - DecommissioningFailedException e = expectThrows(DecommissioningFailedException.class, () -> { - decommissionService.initiateAttributeDecommissioning( - decommissionAttribute, listener, clusterService.state()); - }); + DecommissioningFailedException e = expectThrows( + DecommissioningFailedException.class, + () -> { decommissionService.initiateAttributeDecommissioning(decommissionAttribute, listener, clusterService.state()); } + ); assertThat(e.getMessage(), Matchers.endsWith("invalid awareness attribute requested for decommissioning")); } @@ -129,13 +126,15 @@ public void testDecommissioningNotInitiatedForInvalidAttributeName() { public void testDecommissioningNotInitiatedForInvalidAttributeValue() { DecommissionAttribute decommissionAttribute = new DecommissionAttribute("zone", "random"); ActionListener listener = mock(ActionListener.class); - DecommissioningFailedException e = expectThrows(DecommissioningFailedException.class, () -> { - decommissionService.initiateAttributeDecommissioning( - decommissionAttribute, listener, clusterService.state()); - }); + DecommissioningFailedException e = expectThrows( + DecommissioningFailedException.class, + () -> { decommissionService.initiateAttributeDecommissioning(decommissionAttribute, listener, clusterService.state()); } + ); assertThat( e.getMessage(), - Matchers.endsWith("invalid awareness attribute value requested for decommissioning. Set forced awareness values before to decommission") + Matchers.endsWith( + "invalid awareness attribute value requested for decommissioning. Set forced awareness values before to decommission" + ) ); } @@ -149,14 +148,20 @@ public void testDecommissioningNotInitiatedWhenAlreadyDecommissioned() { setState( clusterService, builder.metadata( - Metadata.builder( - clusterService.state().metadata()).putCustom(DecommissionAttributeMetadata.TYPE, oldMetadata).build() - )); + Metadata.builder(clusterService.state().metadata()).putCustom(DecommissionAttributeMetadata.TYPE, oldMetadata).build() + ) + ); ActionListener listener = mock(ActionListener.class); - DecommissioningFailedException e = expectThrows(DecommissioningFailedException.class, () -> { - decommissionService.initiateAttributeDecommissioning( - new DecommissionAttribute("zone", "zone_2"), listener, clusterService.state()); - }); + DecommissioningFailedException e = expectThrows( + DecommissioningFailedException.class, + () -> { + decommissionService.initiateAttributeDecommissioning( + new DecommissionAttribute("zone", "zone_2"), + listener, + clusterService.state() + ); + } + ); assertThat( e.getMessage(), Matchers.endsWith("one awareness attribute already decommissioned, recommission before triggering another decommission") @@ -180,15 +185,18 @@ private ClusterState setLocalNodeAsClusterManagerNode(ClusterState clusterState, private ClusterState setThreeNodesInVotingConfig(ClusterState clusterState) { final CoordinationMetadata.VotingConfiguration votingConfiguration = CoordinationMetadata.VotingConfiguration.of( - clusterState.nodes().get("node1"), clusterState.nodes().get("node6"), clusterState.nodes().get("node11") + clusterState.nodes().get("node1"), + clusterState.nodes().get("node6"), + clusterState.nodes().get("node11") ); - Metadata.Builder builder = Metadata.builder().coordinationMetadata( - CoordinationMetadata.builder() - .lastAcceptedConfiguration(votingConfiguration) - .lastCommittedConfiguration(votingConfiguration) - .build() - ); + Metadata.Builder builder = Metadata.builder() + .coordinationMetadata( + CoordinationMetadata.builder() + .lastAcceptedConfiguration(votingConfiguration) + .lastCommittedConfiguration(votingConfiguration) + .build() + ); clusterState = ClusterState.builder(clusterState).metadata(builder).build(); return clusterState; } diff --git a/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataSerializationTests.java b/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataSerializationTests.java index 5423c2ed672a3..60b3a03848830 100644 --- a/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataSerializationTests.java +++ b/server/src/test/java/org/opensearch/cluster/metadata/DecommissionAttributeMetadataSerializationTests.java @@ -54,13 +54,10 @@ protected Metadata.Custom makeTestChanges(Metadata.Custom testInstance) { if (randomBoolean()) { attributeName = randomAlphaOfLength(6); } - if(randomBoolean()) { + if (randomBoolean()) { attributeValue = randomAlphaOfLength(6); } - return new DecommissionAttributeMetadata( - new DecommissionAttribute(attributeName, attributeValue), - decommissionStatus - ); + return new DecommissionAttributeMetadata(new DecommissionAttribute(attributeName, attributeValue), decommissionStatus); } @Override From 98ba562f6b1446f4c25a0104f68a43f1d304ff20 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 30 Aug 2022 15:56:26 +0530 Subject: [PATCH 150/187] Update enum Signed-off-by: Rishab Nahata --- server/src/main/java/org/opensearch/OpenSearchException.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/org/opensearch/OpenSearchException.java b/server/src/main/java/org/opensearch/OpenSearchException.java index 7799aef7fab38..83a11ba10da56 100644 --- a/server/src/main/java/org/opensearch/OpenSearchException.java +++ b/server/src/main/java/org/opensearch/OpenSearchException.java @@ -1611,7 +1611,7 @@ private enum OpenSearchExceptionHandle { 162, V_3_0_0 ), - DECOMMISSION_FAILED_EXCEPTION( + DECOMMISSIONING_FAILED_EXCEPTION( org.opensearch.cluster.decommission.DecommissioningFailedException.class, org.opensearch.cluster.decommission.DecommissioningFailedException::new, 163, From a6994495327b4ad94c7dafd950ed6680dfce24a3 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 30 Aug 2022 16:11:01 +0530 Subject: [PATCH 151/187] Fix spotless and precommit checks Signed-off-by: Rishab Nahata --- server/src/main/java/org/opensearch/OpenSearchException.java | 1 - 1 file changed, 1 deletion(-) diff --git a/server/src/main/java/org/opensearch/OpenSearchException.java b/server/src/main/java/org/opensearch/OpenSearchException.java index 83a11ba10da56..932aae741160e 100644 --- a/server/src/main/java/org/opensearch/OpenSearchException.java +++ b/server/src/main/java/org/opensearch/OpenSearchException.java @@ -34,7 +34,6 @@ import org.opensearch.action.support.replication.ReplicationOperation; import org.opensearch.cluster.action.shard.ShardStateAction; -import org.opensearch.cluster.decommission.DecommissioningFailedException; import org.opensearch.common.CheckedFunction; import org.opensearch.common.Nullable; import org.opensearch.common.ParseField; From fdade2016f9870ccb28f60e7069c1340cd3b1e6f Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 30 Aug 2022 20:07:39 +0530 Subject: [PATCH 152/187] Add package-info and Changelog Signed-off-by: Rishab Nahata --- CHANGELOG.md | 3 ++- .../cluster/decommission/package-info.java | 12 ++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 server/src/main/java/org/opensearch/cluster/decommission/package-info.java diff --git a/CHANGELOG.md b/CHANGELOG.md index ec8441831448f..9bb524a7fba3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Plugin ZIP publication groupId value is configurable ([#4156](https://github.com/opensearch-project/OpenSearch/pull/4156)) - Add index specific setting for remote repository ([#4253](https://github.com/opensearch-project/OpenSearch/pull/4253)) - [Segment Replication] Update replicas to commit SegmentInfos instead of relying on SIS files from primary shards. ([#4402](https://github.com/opensearch-project/OpenSearch/pull/4402)) +- Add DecommissionService and helper to execute awareness attribute decommissioning ([#4084](https://github.com/opensearch-project/OpenSearch/pull/4084)) ### Deprecated @@ -78,4 +79,4 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) [Unreleased]: https://github.com/opensearch-project/OpenSearch/compare/2.2.0...HEAD -[2.x]: https://github.com/opensearch-project/OpenSearch/compare/2.2.0...2.x \ No newline at end of file +[2.x]: https://github.com/opensearch-project/OpenSearch/compare/2.2.0...2.x diff --git a/server/src/main/java/org/opensearch/cluster/decommission/package-info.java b/server/src/main/java/org/opensearch/cluster/decommission/package-info.java new file mode 100644 index 0000000000000..256c2f22253cc --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/decommission/package-info.java @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * Decommission lifecycle classes + */ +package org.opensearch.cluster.decommission; From 058a0d8b075a3e24e49c4d41d3a9601bec1771d9 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Thu, 1 Sep 2022 17:28:43 +0530 Subject: [PATCH 153/187] Add checks for quorum Signed-off-by: Rishab Nahata --- .../decommission/DecommissionService.java | 38 ++++++-- .../DecommissionServiceTests.java | 88 ++++++++++++++----- 2 files changed, 98 insertions(+), 28 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 89ddabb9fa19e..e3e76a1e45086 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -16,6 +16,8 @@ import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.NotClusterManagerException; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; +import org.opensearch.cluster.coordination.ClusterBootstrapService; +import org.opensearch.cluster.coordination.CoordinationMetadata; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.routing.allocation.AllocationService; @@ -116,15 +118,22 @@ public void initiateAttributeDecommissioning( // action validateAwarenessAttribute(decommissionAttribute, awarenessAttributes, forcedAwarenessAttributes); + Set clusterManagerNodesToBeDecommissioned = nodesWithDecommissionAttribute(state, decommissionAttribute, true); + ensureNoQuorumLossDueToDecommissioning( + decommissionAttribute, + clusterManagerNodesToBeDecommissioned, + state.getLastAcceptedConfiguration() + ); + DecommissionAttributeMetadata decommissionAttributeMetadata = state.metadata().custom(DecommissionAttributeMetadata.TYPE); // validates that there's no inflight decommissioning or already executed decommission in place ensureNoAwarenessAttributeDecommissioned(decommissionAttributeMetadata, decommissionAttribute); logger.info("initiating awareness attribute [{}] decommissioning", decommissionAttribute.toString()); - // remove all decommissioned cluster manager eligible nodes from voting config + // remove all 'to-be-decommissioned' cluster manager eligible nodes from voting config // The method ensures that we don't exclude same nodes multiple times - excludeDecommissionedClusterManagerNodesFromVotingConfig(decommissionAttribute); + excludeDecommissionedClusterManagerNodesFromVotingConfig(clusterManagerNodesToBeDecommissioned); // explicitly throwing NotClusterManagerException as we can certainly say the local cluster manager node will // be abdicated and soon will no longer be cluster manager. @@ -140,12 +149,7 @@ public void initiateAttributeDecommissioning( } } - private void excludeDecommissionedClusterManagerNodesFromVotingConfig(DecommissionAttribute decommissionAttribute) { - Set clusterManagerNodesToBeDecommissioned = nodesWithDecommissionAttribute( - clusterService.state(), - decommissionAttribute, - true - ); + private void excludeDecommissionedClusterManagerNodesFromVotingConfig(Set clusterManagerNodesToBeDecommissioned) { Set clusterManagerNodesNameToBeDecommissioned = clusterManagerNodesToBeDecommissioned.stream() .map(DiscoveryNode::getName) .collect(Collectors.toSet()); @@ -410,4 +414,22 @@ private static void ensureNoAwarenessAttributeDecommissioned( ); } } + + private static void ensureNoQuorumLossDueToDecommissioning( + DecommissionAttribute decommissionAttribute, + Set clusterManagerNodesToBeDecommissioned, + CoordinationMetadata.VotingConfiguration votingConfiguration + ) { + final Set nodesInVotingConfig = votingConfiguration.getNodeIds(); + assert nodesInVotingConfig.isEmpty() == false; + final int requiredNodes = nodesInVotingConfig.size() / 2 + 1; + final Set realNodeIds = new HashSet<>(nodesInVotingConfig); + realNodeIds.removeIf(ClusterBootstrapService::isBootstrapPlaceholder); + if (realNodeIds.size() - clusterManagerNodesToBeDecommissioned.size() < requiredNodes) { + throw new DecommissioningFailedException( + decommissionAttribute, + "cannot proceed with decommission request. Cluster might go into quorum loss" + ); + } + } } diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java index 04bb876761113..ea4d9eb76b0dc 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java @@ -32,7 +32,6 @@ import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; -import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Map; @@ -67,14 +66,25 @@ public void setUp() throws Exception { @Before public void setUpService() { ClusterState clusterState = ClusterState.builder(new ClusterName("test")).build(); - logger.info("--> adding five nodes on same zone_1"); - clusterState = addNodes(clusterState, "zone_1", "node1", "node2", "node3", "node4", "node5"); - logger.info("--> adding five nodes on same zone_2"); - clusterState = addNodes(clusterState, "zone_2", "node6", "node7", "node8", "node9", "node10"); - logger.info("--> adding five nodes on same zone_3"); - clusterState = addNodes(clusterState, "zone_3", "node11", "node12", "node13", "node14", "node15"); + logger.info("--> adding cluster manager node on zone_1"); + clusterState = addClusterManagerNodes(clusterState, "zone_1", "node1"); + logger.info("--> adding cluster manager node on zone_2"); + clusterState = addClusterManagerNodes(clusterState, "zone_2", "node6"); + logger.info("--> adding cluster manager node on zone_3"); + clusterState = addClusterManagerNodes(clusterState, "zone_3", "node11"); + logger.info("--> adding four data nodes on zone_1"); + clusterState = addDataNodes(clusterState, "zone_1", "node2", "node3", "node4", "node5"); + logger.info("--> adding four data nodes on zone_2"); + clusterState = addDataNodes(clusterState, "zone_2", "node7", "node8", "node9", "node10"); + logger.info("--> adding four data nodes on zone_3"); + clusterState = addDataNodes(clusterState, "zone_3", "node12", "node13", "node14", "node15"); clusterState = setLocalNodeAsClusterManagerNode(clusterState, "node1"); - clusterState = setThreeNodesInVotingConfig(clusterState); + clusterState = setNodesInVotingConfig( + clusterState, + clusterState.nodes().get("node1"), + clusterState.nodes().get("node6"), + clusterState.nodes().get("node11") + ); final ClusterState.Builder builder = builder(clusterState); setState(clusterService, builder); final MockTransport transport = new MockTransport(); @@ -168,9 +178,37 @@ public void testDecommissioningNotInitiatedWhenAlreadyDecommissioned() { ); } - private ClusterState addNodes(ClusterState clusterState, String zone, String... nodeIds) { + @SuppressWarnings("unchecked") + public void testDecommissioningNotInitiatedWhenNotEnoughClusterManagerNodes() { + ClusterState state = clusterService.state(); + // shrink voting config + state = setNodesInVotingConfig(state, state.nodes().get("node1"), state.nodes().get("node11")); + setState(clusterService, state); + ActionListener listener = mock(ActionListener.class); + DecommissioningFailedException e = expectThrows( + DecommissioningFailedException.class, + () -> { + decommissionService.initiateAttributeDecommissioning( + new DecommissionAttribute("zone", "zone_3"), + listener, + clusterService.state() + ); + } + ); + assertThat(e.getMessage(), Matchers.endsWith("cannot proceed with decommission request. Cluster might go into quorum loss")); + } + + private ClusterState addDataNodes(ClusterState clusterState, String zone, String... nodeIds) { + DiscoveryNodes.Builder nodeBuilder = DiscoveryNodes.builder(clusterState.nodes()); + org.opensearch.common.collect.List.of(nodeIds).forEach(nodeId -> nodeBuilder.add(newDataNode(nodeId, singletonMap("zone", zone)))); + clusterState = ClusterState.builder(clusterState).nodes(nodeBuilder).build(); + return clusterState; + } + + private ClusterState addClusterManagerNodes(ClusterState clusterState, String zone, String... nodeIds) { DiscoveryNodes.Builder nodeBuilder = DiscoveryNodes.builder(clusterState.nodes()); - org.opensearch.common.collect.List.of(nodeIds).forEach(nodeId -> nodeBuilder.add(newNode(nodeId, singletonMap("zone", zone)))); + org.opensearch.common.collect.List.of(nodeIds) + .forEach(nodeId -> nodeBuilder.add(newClusterManagerNode(nodeId, singletonMap("zone", zone)))); clusterState = ClusterState.builder(clusterState).nodes(nodeBuilder).build(); return clusterState; } @@ -183,12 +221,8 @@ private ClusterState setLocalNodeAsClusterManagerNode(ClusterState clusterState, return clusterState; } - private ClusterState setThreeNodesInVotingConfig(ClusterState clusterState) { - final CoordinationMetadata.VotingConfiguration votingConfiguration = CoordinationMetadata.VotingConfiguration.of( - clusterState.nodes().get("node1"), - clusterState.nodes().get("node6"), - clusterState.nodes().get("node11") - ); + private ClusterState setNodesInVotingConfig(ClusterState clusterState, DiscoveryNode... nodes) { + final CoordinationMetadata.VotingConfiguration votingConfiguration = CoordinationMetadata.VotingConfiguration.of(nodes); Metadata.Builder builder = Metadata.builder() .coordinationMetadata( @@ -201,11 +235,25 @@ private ClusterState setThreeNodesInVotingConfig(ClusterState clusterState) { return clusterState; } - private static DiscoveryNode newNode(String nodeId, Map attributes) { - return new DiscoveryNode(nodeId, buildNewFakeTransportAddress(), attributes, CLUSTER_MANAGER_DATA_ROLE, Version.CURRENT); + private static DiscoveryNode newDataNode(String nodeId, Map attributes) { + return new DiscoveryNode(nodeId, buildNewFakeTransportAddress(), attributes, DATA_ROLE, Version.CURRENT); + } + + private static DiscoveryNode newClusterManagerNode(String nodeId, Map attributes) { + return new DiscoveryNode(nodeId, buildNewFakeTransportAddress(), attributes, CLUSTER_MANAGER_ROLE, Version.CURRENT); } - final private static Set CLUSTER_MANAGER_DATA_ROLE = Collections.unmodifiableSet( - new HashSet<>(Arrays.asList(DiscoveryNodeRole.CLUSTER_MANAGER_ROLE, DiscoveryNodeRole.DATA_ROLE)) + final private static Set CLUSTER_MANAGER_ROLE = Collections.unmodifiableSet( + new HashSet<>(Collections.singletonList(DiscoveryNodeRole.CLUSTER_MANAGER_ROLE)) ); + + final private static Set DATA_ROLE = Collections.unmodifiableSet( + new HashSet<>(Collections.singletonList(DiscoveryNodeRole.DATA_ROLE)) + ); + + private ClusterState removeNodes(ClusterState clusterState, String... nodeIds) { + DiscoveryNodes.Builder nodeBuilder = DiscoveryNodes.builder(clusterState.getNodes()); + org.opensearch.common.collect.List.of(nodeIds).forEach(nodeBuilder::remove); + return allocationService.disassociateDeadNodes(ClusterState.builder(clusterState).nodes(nodeBuilder).build(), false, "test"); + } } From 1c235467b28a243b8cecdb4aa3ec1984458679a4 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Thu, 1 Sep 2022 17:52:19 +0530 Subject: [PATCH 154/187] Bug fix Signed-off-by: Rishab Nahata --- .../org/opensearch/cluster/decommission/DecommissionService.java | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index e3e76a1e45086..e37915d0924a9 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -425,6 +425,7 @@ private static void ensureNoQuorumLossDueToDecommissioning( final int requiredNodes = nodesInVotingConfig.size() / 2 + 1; final Set realNodeIds = new HashSet<>(nodesInVotingConfig); realNodeIds.removeIf(ClusterBootstrapService::isBootstrapPlaceholder); + clusterManagerNodesToBeDecommissioned.removeIf(b -> !nodesInVotingConfig.contains(b.getId())); if (realNodeIds.size() - clusterManagerNodesToBeDecommissioned.size() < requiredNodes) { throw new DecommissioningFailedException( decommissionAttribute, From ffa94508142d2383e0a2adb2faf5315c429479d8 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Fri, 2 Sep 2022 16:16:15 +0530 Subject: [PATCH 155/187] Resolving PR comments Signed-off-by: Rishab Nahata --- .../decommission/DecommissionController.java | 3 +-- .../cluster/decommission/DecommissionService.java | 2 +- .../cluster/decommission/DecommissionStatus.java | 14 +------------- .../coordination/JoinTaskExecutorTests.java | 3 +-- .../decommission/DecommissionControllerTests.java | 4 ++-- 5 files changed, 6 insertions(+), 20 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java index 1799479efe4cc..89790c7cdeef8 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java @@ -102,7 +102,6 @@ public AddVotingConfigExclusionsResponse read(StreamInput in) throws IOException public void clearVotingConfigExclusion(ActionListener listener) { final ClearVotingConfigExclusionsRequest clearVotingConfigExclusionsRequest = new ClearVotingConfigExclusionsRequest(); - clearVotingConfigExclusionsRequest.setWaitForRemoval(true); transportService.sendRequest( transportService.getLocalNode(), ClearVotingConfigExclusionsAction.NAME, @@ -133,7 +132,7 @@ public ClearVotingConfigExclusionsResponse read(StreamInput in) throws IOExcepti ); } - public void handleNodesDecommissionRequest( + public void removeDecommissionedNodes( Set nodesToBeDecommissioned, String reason, TimeValue timeout, diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index e37915d0924a9..e7dbca75ca16b 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -302,7 +302,7 @@ private void failDecommissionedNodes(ClusterState state) { DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute(); // execute nodes decommissioning - decommissionController.handleNodesDecommissionRequest( + decommissionController.removeDecommissionedNodes( nodesWithDecommissionAttribute(state, decommissionAttribute, false), "nodes-decommissioned", TimeValue.timeValueSeconds(30L), // TODO - read timeout from request while integrating with API diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java index 41f9acfbc35d7..1474faa9bb227 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java @@ -27,15 +27,7 @@ public enum DecommissionStatus { /** * Decommission request failed */ - DECOMMISSION_FAILED("decommission_failed"), - /** - * Recommission request received, recommissioning process has started - */ - RECOMMISSION_IN_PROGRESS("recommission_in_progress"), - /** - * Recommission request failed. No nodes should fail to join the cluster with decommission exception - */ - RECOMMISSION_FAILED("recommission_failed"); + DECOMMISSION_FAILED("decommission_failed"); private final String status; @@ -70,10 +62,6 @@ public static DecommissionStatus fromString(String status) { return DECOMMISSION_SUCCESSFUL; } else if (status.equals(DECOMMISSION_FAILED.status())) { return DECOMMISSION_FAILED; - } else if (status.equals(RECOMMISSION_IN_PROGRESS.status())) { - return RECOMMISSION_IN_PROGRESS; - } else if (status.equals(RECOMMISSION_FAILED.status())) { - return RECOMMISSION_FAILED; } throw new IllegalStateException("Decommission status [" + status + "] not recognized."); } diff --git a/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java b/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java index 734f112bdce3d..003a16bc218ef 100644 --- a/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java +++ b/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java @@ -272,8 +272,7 @@ public void testJoinClusterWithDecommissionFailedOrInitOrRecommission() { DecommissionAttribute decommissionAttribute = new DecommissionAttribute("zone", "zone-1"); DecommissionStatus decommissionStatus = randomFrom( DecommissionStatus.DECOMMISSION_INIT, - DecommissionStatus.DECOMMISSION_FAILED, - DecommissionStatus.RECOMMISSION_IN_PROGRESS + DecommissionStatus.DECOMMISSION_FAILED ); DecommissionAttributeMetadata decommissionAttributeMetadata = new DecommissionAttributeMetadata( decommissionAttribute, diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java index e5d7ec60c0e23..8e5d4e61937a4 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java @@ -176,7 +176,7 @@ public void testNodesRemovedForDecommissionRequestSuccessfulResponse() throws In nodesToBeRemoved.add(clusterService.state().nodes().get("node14")); nodesToBeRemoved.add(clusterService.state().nodes().get("node15")); - decommissionController.handleNodesDecommissionRequest( + decommissionController.removeDecommissionedNodes( nodesToBeRemoved, "unit-test", TimeValue.timeValueSeconds(30L), @@ -212,7 +212,7 @@ public void testTimesOut() throws InterruptedException { nodesToBeRemoved.add(clusterService.state().nodes().get("node13")); nodesToBeRemoved.add(clusterService.state().nodes().get("node14")); nodesToBeRemoved.add(clusterService.state().nodes().get("node15")); - decommissionController.handleNodesDecommissionRequest( + decommissionController.removeDecommissionedNodes( nodesToBeRemoved, "unit-test", TimeValue.timeValueMillis(2), From 406f4d55d75f380cee1bce5fdbdb95d81c05684a Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 6 Sep 2022 13:56:58 +0530 Subject: [PATCH 156/187] Update awareness attribute decommission status check Signed-off-by: Rishab Nahata --- .../decommission/DecommissionService.java | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index e7dbca75ca16b..9f7eeeed80ebd 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -127,7 +127,7 @@ public void initiateAttributeDecommissioning( DecommissionAttributeMetadata decommissionAttributeMetadata = state.metadata().custom(DecommissionAttributeMetadata.TYPE); // validates that there's no inflight decommissioning or already executed decommission in place - ensureNoAwarenessAttributeDecommissioned(decommissionAttributeMetadata, decommissionAttribute); + ensureNoInflightDifferentDecommissionRequest(decommissionAttributeMetadata, decommissionAttribute); logger.info("initiating awareness attribute [{}] decommissioning", decommissionAttribute.toString()); @@ -214,7 +214,7 @@ public ClusterState execute(ClusterState currentState) { Metadata metadata = currentState.metadata(); Metadata.Builder mdBuilder = Metadata.builder(metadata); DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.custom(DecommissionAttributeMetadata.TYPE); - ensureNoAwarenessAttributeDecommissioned(decommissionAttributeMetadata, decommissionAttribute); + ensureNoInflightDifferentDecommissionRequest(decommissionAttributeMetadata, decommissionAttribute); decommissionAttributeMetadata = new DecommissionAttributeMetadata(decommissionAttribute); mdBuilder.putCustom(DecommissionAttributeMetadata.TYPE, decommissionAttributeMetadata); logger.info( @@ -401,17 +401,28 @@ private static void validateAwarenessAttribute( } } - private static void ensureNoAwarenessAttributeDecommissioned( + private static void ensureNoInflightDifferentDecommissionRequest( DecommissionAttributeMetadata decommissionAttributeMetadata, DecommissionAttribute decommissionAttribute ) { - // If the previous decommission request failed, we will allow the request to pass this check - if (decommissionAttributeMetadata != null - && !decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_FAILED)) { - throw new DecommissioningFailedException( - decommissionAttribute, - "one awareness attribute already decommissioned, recommission before triggering another decommission" - ); + String msg = null; + if (decommissionAttributeMetadata!=null) { + if (decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_SUCCESSFUL)) { + // one awareness attribute is already decommissioned. We will reject the new request + msg = "one awareness attribute already successfully decommissioned. Recommission before triggering another decommission"; + } else if (decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_FAILED)) { + // here we are sure that the previous decommission request failed, we can let this request pass this check + return; + } else { + // it means the decommission has been initiated or is inflight. In that case, if the same attribute is requested for decommissioning, + // which can happen during retries, we will pass this check, if not, we will throw exception + if (!decommissionAttributeMetadata.decommissionAttribute().equals(decommissionAttribute)) { + msg = "another request for decommission is in flight, will not process this request"; + } + } + } + if (msg != null) { + throw new DecommissioningFailedException(decommissionAttribute, msg); } } From f616d0e08a0ae54647dbb32beb2dd4d4dae3c029 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 6 Sep 2022 15:06:20 +0530 Subject: [PATCH 157/187] Update quorum loss check logic Signed-off-by: Rishab Nahata --- .../decommission/DecommissionService.java | 19 ++++++++------- .../DecommissionServiceTests.java | 24 +++++++++++-------- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 9f7eeeed80ebd..96fe9f8740461 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -431,16 +431,19 @@ private static void ensureNoQuorumLossDueToDecommissioning( Set clusterManagerNodesToBeDecommissioned, CoordinationMetadata.VotingConfiguration votingConfiguration ) { - final Set nodesInVotingConfig = votingConfiguration.getNodeIds(); - assert nodesInVotingConfig.isEmpty() == false; - final int requiredNodes = nodesInVotingConfig.size() / 2 + 1; - final Set realNodeIds = new HashSet<>(nodesInVotingConfig); - realNodeIds.removeIf(ClusterBootstrapService::isBootstrapPlaceholder); - clusterManagerNodesToBeDecommissioned.removeIf(b -> !nodesInVotingConfig.contains(b.getId())); - if (realNodeIds.size() - clusterManagerNodesToBeDecommissioned.size() < requiredNodes) { + Set clusterManagerNodesIdToBeDecommissioned = new HashSet<>(); + clusterManagerNodesToBeDecommissioned.forEach(node -> clusterManagerNodesIdToBeDecommissioned.add(node.getId())); + if (!votingConfiguration.hasQuorum( + votingConfiguration.getNodeIds() + .stream() + .filter(n -> clusterManagerNodesIdToBeDecommissioned.contains(n) == false) + .collect(Collectors.toList() + ) + ) + ) { throw new DecommissioningFailedException( decommissionAttribute, - "cannot proceed with decommission request. Cluster might go into quorum loss" + "cannot proceed with decommission request as cluster might go into quorum loss" ); } } diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java index ea4d9eb76b0dc..126bd605425e3 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java @@ -54,17 +54,11 @@ public class DecommissionServiceTests extends OpenSearchTestCase { private DecommissionService decommissionService; private ClusterSettings clusterSettings; - @Override - public void setUp() throws Exception { - super.setUp(); - super.setUp(); + @Before + public void setUpService() { threadPool = new TestThreadPool("test", Settings.EMPTY); clusterService = createClusterService(threadPool); allocationService = createAllocationService(); - } - - @Before - public void setUpService() { ClusterState clusterState = ClusterState.builder(new ClusterName("test")).build(); logger.info("--> adding cluster manager node on zone_1"); clusterState = addClusterManagerNodes(clusterState, "zone_1", "node1"); @@ -174,7 +168,17 @@ public void testDecommissioningNotInitiatedWhenAlreadyDecommissioned() { ); assertThat( e.getMessage(), - Matchers.endsWith("one awareness attribute already decommissioned, recommission before triggering another decommission") + Matchers.endsWith("another request for decommission is in flight, will not process this request") + ); + } + + @SuppressWarnings("unchecked") + public void testDecommissioningInitiatedWhenEnoughClusterManagerNodes() { + ActionListener listener = mock(ActionListener.class); + decommissionService.initiateAttributeDecommissioning( + new DecommissionAttribute("zone", "zone_3"), + listener, + clusterService.state() ); } @@ -195,7 +199,7 @@ public void testDecommissioningNotInitiatedWhenNotEnoughClusterManagerNodes() { ); } ); - assertThat(e.getMessage(), Matchers.endsWith("cannot proceed with decommission request. Cluster might go into quorum loss")); + assertThat(e.getMessage(), Matchers.endsWith("cannot proceed with decommission request as cluster might go into quorum loss")); } private ClusterState addDataNodes(ClusterState clusterState, String zone, String... nodeIds) { From 9d326e4ec36e3de480d39fa5daf24754bfac71a9 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 6 Sep 2022 16:24:05 +0530 Subject: [PATCH 158/187] Update status assertion and clear voting config for failed init Signed-off-by: Rishab Nahata --- .../decommission/DecommissionController.java | 25 ++++++++------ .../decommission/DecommissionService.java | 34 ++++++++++++++----- 2 files changed, 40 insertions(+), 19 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java index 89790c7cdeef8..25c600ec47add 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java @@ -109,13 +109,11 @@ public void clearVotingConfigExclusion(ActionListener listener) { new TransportResponseHandler() { @Override public void handleResponse(ClearVotingConfigExclusionsResponse response) { - logger.info("successfully cleared voting config after decommissioning"); listener.onResponse(null); } @Override public void handleException(TransportException exp) { - logger.debug(new ParameterizedMessage("failure in clearing voting config exclusion after decommissioning"), exp); listener.onFailure(exp); } @@ -196,7 +194,7 @@ public ClusterState execute(ClusterState currentState) { Metadata metadata = currentState.metadata(); DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.custom(DecommissionAttributeMetadata.TYPE); assert decommissionAttributeMetadata != null && decommissionAttributeMetadata.decommissionAttribute() != null; - assert assertIncrementalStatusOrFailed(decommissionAttributeMetadata.status(), decommissionStatus); + assert assertStatusTransitionOrFailed(decommissionAttributeMetadata.status(), decommissionStatus); Metadata.Builder mdBuilder = Metadata.builder(metadata); DecommissionAttributeMetadata newMetadata = decommissionAttributeMetadata.withUpdatedStatus(decommissionStatus); mdBuilder.putCustom(DecommissionAttributeMetadata.TYPE, newMetadata); @@ -218,13 +216,20 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS }); } - private static boolean assertIncrementalStatusOrFailed(DecommissionStatus oldStatus, DecommissionStatus newStatus) { - if (newStatus.equals(DecommissionStatus.DECOMMISSION_FAILED)) return true; - else if (newStatus.equals(DecommissionStatus.DECOMMISSION_SUCCESSFUL)) { - return oldStatus.equals(DecommissionStatus.DECOMMISSION_IN_PROGRESS); - } else if (newStatus.equals(DecommissionStatus.DECOMMISSION_IN_PROGRESS)) { - return oldStatus.equals(DecommissionStatus.DECOMMISSION_INIT); + private static boolean assertStatusTransitionOrFailed(DecommissionStatus oldStatus, DecommissionStatus newStatus) { + switch (newStatus) { + case DECOMMISSION_INIT: + // if the new status is INIT, then the old status cannot be anything but FAILED + return oldStatus.equals(DecommissionStatus.DECOMMISSION_FAILED); + case DECOMMISSION_IN_PROGRESS: + // if the new status is IN_PROGRESS, the old status has to be INIT + return oldStatus.equals(DecommissionStatus.DECOMMISSION_INIT); + case DECOMMISSION_SUCCESSFUL: + // if the new status is SUCCESSFUL, the old status has to be IN_PROGRESS + return oldStatus.equals(DecommissionStatus.DECOMMISSION_IN_PROGRESS); + default: + // if the new status is FAILED, we don't need to assert for previous state + return true; } - return true; } } diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 96fe9f8740461..0bf6be700a0ee 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -16,7 +16,6 @@ import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.NotClusterManagerException; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; -import org.opensearch.cluster.coordination.ClusterBootstrapService; import org.opensearch.cluster.coordination.CoordinationMetadata; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; @@ -227,13 +226,7 @@ public ClusterState execute(ClusterState currentState) { @Override public void onFailure(String source, Exception e) { - if (e instanceof DecommissioningFailedException) { - logger.error( - () -> new ParameterizedMessage("failed to decommission attribute [{}]", decommissionAttribute.toString()), - e - ); - listener.onFailure(e); - } else if (e instanceof NotClusterManagerException) { + if (e instanceof NotClusterManagerException) { logger.debug( () -> new ParameterizedMessage( "cluster-manager updated while executing request for decommission attribute [{}]", @@ -250,7 +243,7 @@ public void onFailure(String source, Exception e) { ), e ); - listener.onFailure(e); + failAndClearVotingConfigExclusion(listener, e); } } @@ -267,6 +260,24 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS ); } + private void failAndClearVotingConfigExclusion(final ActionListener listener, Exception e) { + decommissionController.clearVotingConfigExclusion(new ActionListener() { + @Override + public void onResponse(Void unused) { + logger.info("successfully cleared voting config exclusion after failing to execute decommission request"); + } + + @Override + public void onFailure(Exception e) { + logger.debug(new ParameterizedMessage( + "failure in clearing voting config exclusion after failing to execute decommission request"), + e + ); + } + }); + listener.onFailure(e); + } + private void initiateGracefulDecommission() { decommissionController.updateMetadataWithDecommissionStatus( DecommissionStatus.DECOMMISSION_IN_PROGRESS, @@ -338,6 +349,7 @@ public void onFailure(Exception e) { decommissionController.clearVotingConfigExclusion(new ActionListener() { @Override public void onResponse(Void unused) { + logger.info("successfully cleared voting config exclusion after failing to execute decommission request"); DecommissionStatus updateStatusWith = decommissionSuccessful ? DecommissionStatus.DECOMMISSION_SUCCESSFUL : DecommissionStatus.DECOMMISSION_FAILED; @@ -346,6 +358,10 @@ public void onResponse(Void unused) { @Override public void onFailure(Exception e) { + logger.debug(new ParameterizedMessage( + "failure in clearing voting config exclusion after processing decommission request"), + e + ); decommissionController.updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_FAILED, statusUpdateListener); } }); From 8731ada4b69c56a3aaab8b06e7e26bbbc533400f Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 6 Sep 2022 16:45:50 +0530 Subject: [PATCH 159/187] Refactoring Signed-off-by: Rishab Nahata --- .../decommission/DecommissionService.java | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 0bf6be700a0ee..d5afcda667115 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -117,7 +117,7 @@ public void initiateAttributeDecommissioning( // action validateAwarenessAttribute(decommissionAttribute, awarenessAttributes, forcedAwarenessAttributes); - Set clusterManagerNodesToBeDecommissioned = nodesWithDecommissionAttribute(state, decommissionAttribute, true); + Set clusterManagerNodesToBeDecommissioned = filterNodesWithDecommissionAttribute(state, decommissionAttribute, true); ensureNoQuorumLossDueToDecommissioning( decommissionAttribute, clusterManagerNodesToBeDecommissioned, @@ -254,7 +254,7 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS assert decommissionAttribute.equals(decommissionAttributeMetadata.decommissionAttribute()); assert DecommissionStatus.DECOMMISSION_INIT.equals(decommissionAttributeMetadata.status()); listener.onResponse(new ClusterStateUpdateResponse(true)); - initiateGracefulDecommission(); + weighAwayForGracefulDecommission(); } } ); @@ -278,7 +278,7 @@ public void onFailure(Exception e) { listener.onFailure(e); } - private void initiateGracefulDecommission() { + private void weighAwayForGracefulDecommission() { decommissionController.updateMetadataWithDecommissionStatus( DecommissionStatus.DECOMMISSION_IN_PROGRESS, new ActionListener() { @@ -314,7 +314,7 @@ private void failDecommissionedNodes(ClusterState state) { // execute nodes decommissioning decommissionController.removeDecommissionedNodes( - nodesWithDecommissionAttribute(state, decommissionAttribute, false), + filterNodesWithDecommissionAttribute(state, decommissionAttribute, false), "nodes-decommissioned", TimeValue.timeValueSeconds(30L), // TODO - read timeout from request while integrating with API new ActionListener() { @@ -367,23 +367,19 @@ public void onFailure(Exception e) { }); } - public Set nodesWithDecommissionAttribute( + private Set filterNodesWithDecommissionAttribute( ClusterState clusterState, DecommissionAttribute decommissionAttribute, boolean onlyClusterManagerNodes ) { Set nodesWithDecommissionAttribute = new HashSet<>(); - final Predicate shouldDecommissionNodePredicate = discoveryNode -> nodeHasDecommissionedAttribute( - discoveryNode, - decommissionAttribute - ); Iterator nodesIter = onlyClusterManagerNodes ? clusterState.nodes().getClusterManagerNodes().valuesIt() : clusterState.nodes().getNodes().valuesIt(); while (nodesIter.hasNext()) { final DiscoveryNode node = nodesIter.next(); - if (shouldDecommissionNodePredicate.test(node)) { + if (nodeHasDecommissionedAttribute(node, decommissionAttribute)) { nodesWithDecommissionAttribute.add(node); } } From f29d7671d6141549397f74e86bc9880ef453454d Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 6 Sep 2022 17:12:29 +0530 Subject: [PATCH 160/187] Fix spotless check Signed-off-by: Rishab Nahata --- .../decommission/DecommissionController.java | 1 - .../decommission/DecommissionService.java | 21 ++++++++----------- .../coordination/JoinTaskExecutorTests.java | 5 +---- .../DecommissionServiceTests.java | 11 ++-------- 4 files changed, 12 insertions(+), 26 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java index 25c600ec47add..579ca508bd1d9 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java @@ -10,7 +10,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.message.ParameterizedMessage; import org.opensearch.OpenSearchTimeoutException; import org.opensearch.action.ActionListener; import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsAction; diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index d5afcda667115..78fb555ac5546 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -36,7 +36,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.function.Predicate; import java.util.stream.Collectors; import static org.opensearch.cluster.routing.allocation.decider.AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING; @@ -269,8 +268,8 @@ public void onResponse(Void unused) { @Override public void onFailure(Exception e) { - logger.debug(new ParameterizedMessage( - "failure in clearing voting config exclusion after failing to execute decommission request"), + logger.debug( + new ParameterizedMessage("failure in clearing voting config exclusion after failing to execute decommission request"), e ); } @@ -358,8 +357,8 @@ public void onResponse(Void unused) { @Override public void onFailure(Exception e) { - logger.debug(new ParameterizedMessage( - "failure in clearing voting config exclusion after processing decommission request"), + logger.debug( + new ParameterizedMessage("failure in clearing voting config exclusion after processing decommission request"), e ); decommissionController.updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_FAILED, statusUpdateListener); @@ -418,7 +417,7 @@ private static void ensureNoInflightDifferentDecommissionRequest( DecommissionAttribute decommissionAttribute ) { String msg = null; - if (decommissionAttributeMetadata!=null) { + if (decommissionAttributeMetadata != null) { if (decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_SUCCESSFUL)) { // one awareness attribute is already decommissioned. We will reject the new request msg = "one awareness attribute already successfully decommissioned. Recommission before triggering another decommission"; @@ -426,8 +425,8 @@ private static void ensureNoInflightDifferentDecommissionRequest( // here we are sure that the previous decommission request failed, we can let this request pass this check return; } else { - // it means the decommission has been initiated or is inflight. In that case, if the same attribute is requested for decommissioning, - // which can happen during retries, we will pass this check, if not, we will throw exception + // it means the decommission has been initiated or is inflight. In that case, if the same attribute is requested for + // decommissioning, which can happen during retries, we will pass this check, if not, we will throw exception if (!decommissionAttributeMetadata.decommissionAttribute().equals(decommissionAttribute)) { msg = "another request for decommission is in flight, will not process this request"; } @@ -449,10 +448,8 @@ private static void ensureNoQuorumLossDueToDecommissioning( votingConfiguration.getNodeIds() .stream() .filter(n -> clusterManagerNodesIdToBeDecommissioned.contains(n) == false) - .collect(Collectors.toList() - ) - ) - ) { + .collect(Collectors.toList()) + )) { throw new DecommissioningFailedException( decommissionAttribute, "cannot proceed with decommission request as cluster might go into quorum loss" diff --git a/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java b/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java index 003a16bc218ef..e9ad305637f95 100644 --- a/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java +++ b/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java @@ -270,10 +270,7 @@ public void testJoinClusterWithDifferentDecommission() { public void testJoinClusterWithDecommissionFailedOrInitOrRecommission() { Settings.builder().build(); DecommissionAttribute decommissionAttribute = new DecommissionAttribute("zone", "zone-1"); - DecommissionStatus decommissionStatus = randomFrom( - DecommissionStatus.DECOMMISSION_INIT, - DecommissionStatus.DECOMMISSION_FAILED - ); + DecommissionStatus decommissionStatus = randomFrom(DecommissionStatus.DECOMMISSION_INIT, DecommissionStatus.DECOMMISSION_FAILED); DecommissionAttributeMetadata decommissionAttributeMetadata = new DecommissionAttributeMetadata( decommissionAttribute, decommissionStatus diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java index 126bd605425e3..df148c6201d3c 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java @@ -166,20 +166,13 @@ public void testDecommissioningNotInitiatedWhenAlreadyDecommissioned() { ); } ); - assertThat( - e.getMessage(), - Matchers.endsWith("another request for decommission is in flight, will not process this request") - ); + assertThat(e.getMessage(), Matchers.endsWith("another request for decommission is in flight, will not process this request")); } @SuppressWarnings("unchecked") public void testDecommissioningInitiatedWhenEnoughClusterManagerNodes() { ActionListener listener = mock(ActionListener.class); - decommissionService.initiateAttributeDecommissioning( - new DecommissionAttribute("zone", "zone_3"), - listener, - clusterService.state() - ); + decommissionService.initiateAttributeDecommissioning(new DecommissionAttribute("zone", "zone_3"), listener, clusterService.state()); } @SuppressWarnings("unchecked") From ba36f94ff8c6de34357bd319201edbbdf74f860d Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 6 Sep 2022 18:18:53 +0530 Subject: [PATCH 161/187] Resolve comments Signed-off-by: Rishab Nahata --- .../main/java/org/opensearch/OpenSearchException.java | 4 ++-- .../cluster/decommission/DecommissionController.java | 11 +++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/server/src/main/java/org/opensearch/OpenSearchException.java b/server/src/main/java/org/opensearch/OpenSearchException.java index 932aae741160e..2b909271660c5 100644 --- a/server/src/main/java/org/opensearch/OpenSearchException.java +++ b/server/src/main/java/org/opensearch/OpenSearchException.java @@ -1614,13 +1614,13 @@ private enum OpenSearchExceptionHandle { org.opensearch.cluster.decommission.DecommissioningFailedException.class, org.opensearch.cluster.decommission.DecommissioningFailedException::new, 163, - V_2_3_0 + V_3_0_0 ), NODE_DECOMMISSIONED_EXCEPTION( org.opensearch.cluster.decommission.NodeDecommissionedException.class, org.opensearch.cluster.decommission.NodeDecommissionedException::new, 164, - V_2_3_0 + V_3_0_0 ); final Class exceptionClass; diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java index 579ca508bd1d9..dc183613fa162 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java @@ -143,7 +143,7 @@ public void removeDecommissionedNodes( clusterService.submitStateUpdateTasks( "node-decommissioned", nodesDecommissionTasks, - ClusterStateTaskConfig.build(Priority.IMMEDIATE), + ClusterStateTaskConfig.build(Priority.URGENT), nodeRemovalExecutor ); @@ -170,7 +170,7 @@ public void onNewClusterState(ClusterState state) { @Override public void onClusterServiceClose() { - logger.debug("cluster service closed while waiting for removal of decommissioned nodes."); + logger.warn("cluster service closed while waiting for removal of decommissioned nodes."); } @Override @@ -178,7 +178,8 @@ public void onTimeout(TimeValue timeout) { logger.info("timed out while waiting for removal of decommissioned nodes"); nodesRemovedListener.onFailure( new OpenSearchTimeoutException( - "timed out waiting for removal of decommissioned nodes [{}] to take effect", + "timed out [{}] while waiting for removal of decommissioned nodes [{}] to take effect", + timeout.toString(), nodesToBeDecommissioned.toString() ) ); @@ -226,9 +227,11 @@ private static boolean assertStatusTransitionOrFailed(DecommissionStatus oldStat case DECOMMISSION_SUCCESSFUL: // if the new status is SUCCESSFUL, the old status has to be IN_PROGRESS return oldStatus.equals(DecommissionStatus.DECOMMISSION_IN_PROGRESS); - default: + case DECOMMISSION_FAILED: // if the new status is FAILED, we don't need to assert for previous state return true; + default: + throw new IllegalStateException("unexpected status [" + newStatus.status() + "] requested to update"); } } } From ebe3f71a4cc7ff88889c71166c9f25f6a80f365f Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Tue, 6 Sep 2022 18:29:40 +0530 Subject: [PATCH 162/187] Fix spotless check Signed-off-by: Rishab Nahata --- server/src/main/java/org/opensearch/OpenSearchException.java | 1 - 1 file changed, 1 deletion(-) diff --git a/server/src/main/java/org/opensearch/OpenSearchException.java b/server/src/main/java/org/opensearch/OpenSearchException.java index 2b909271660c5..34d7509c7afb2 100644 --- a/server/src/main/java/org/opensearch/OpenSearchException.java +++ b/server/src/main/java/org/opensearch/OpenSearchException.java @@ -68,7 +68,6 @@ import static java.util.Collections.singletonMap; import static java.util.Collections.unmodifiableMap; import static org.opensearch.Version.V_2_1_0; -import static org.opensearch.Version.V_2_3_0; import static org.opensearch.Version.V_3_0_0; import static org.opensearch.cluster.metadata.IndexMetadata.INDEX_UUID_NA_VALUE; import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken; From 22bc424da5dfb2d99597f849a2c24a92a9d7c044 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Wed, 7 Sep 2022 17:02:16 +0530 Subject: [PATCH 163/187] Updating states and flow Signed-off-by: Rishab Nahata --- .../coordination/JoinTaskExecutor.java | 4 +- .../DecommissionAttributeMetadata.java | 4 +- .../decommission/DecommissionController.java | 61 ++-- .../decommission/DecommissionService.java | 290 +++++++++--------- .../decommission/DecommissionStatus.java | 66 +++- .../coordination/JoinTaskExecutorTests.java | 6 +- .../DecommissionControllerTests.java | 8 +- .../DecommissionServiceTests.java | 2 +- 8 files changed, 225 insertions(+), 216 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java index ebf37e21bbfd6..ee2b35eac302d 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java @@ -484,8 +484,8 @@ public static void ensureNodeCommissioned(DiscoveryNode node, Metadata metadata) if (decommissionAttribute != null && status != null) { // We will let the node join the cluster if the current status is not IN_PROGRESS or SUCCESSFUL if (node.getAttributes().get(decommissionAttribute.attributeName()).equals(decommissionAttribute.attributeValue()) - && (status.equals(DecommissionStatus.DECOMMISSION_IN_PROGRESS) - || status.equals(DecommissionStatus.DECOMMISSION_SUCCESSFUL))) { + && (status.equals(DecommissionStatus.IN_PROGRESS) + || status.equals(DecommissionStatus.SUCCESSFUL))) { throw new NodeDecommissionedException( "node [{}] has decommissioned attribute [{}].", node.toString(), diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttributeMetadata.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttributeMetadata.java index 0924a181fb458..fc3f1841615d9 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttributeMetadata.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttributeMetadata.java @@ -50,12 +50,12 @@ public DecommissionAttributeMetadata(DecommissionAttribute decommissionAttribute } /** - * Constructs new decommission attribute metadata with status as {@link DecommissionStatus#DECOMMISSION_INIT} + * Constructs new decommission attribute metadata with status as {@link DecommissionStatus#INIT} * * @param decommissionAttribute attribute details */ public DecommissionAttributeMetadata(DecommissionAttribute decommissionAttribute) { - this(decommissionAttribute, DecommissionStatus.DECOMMISSION_INIT); + this(decommissionAttribute, DecommissionStatus.INIT); } /** diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java index dc183613fa162..9a87a48af60b2 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java @@ -37,11 +37,12 @@ import org.opensearch.transport.TransportService; import java.io.IOException; -import java.util.Iterator; +import java.util.Arrays; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import java.util.function.Predicate; +import java.util.stream.Collectors; /** * Helper controller class to remove list of nodes from the cluster and update status @@ -135,7 +136,7 @@ public void removeDecommissionedNodes( TimeValue timeout, ActionListener nodesRemovedListener ) { - final Map nodesDecommissionTasks = new LinkedHashMap<>(); + final Map nodesDecommissionTasks = new LinkedHashMap<>(nodesToBeDecommissioned.size()); nodesToBeDecommissioned.forEach(discoveryNode -> { final NodeRemovalClusterStateTaskExecutor.Task task = new NodeRemovalClusterStateTaskExecutor.Task(discoveryNode, reason); nodesDecommissionTasks.put(task, nodeRemovalExecutor); @@ -148,15 +149,10 @@ public void removeDecommissionedNodes( ); Predicate allDecommissionedNodesRemovedPredicate = clusterState -> { - Iterator nodesIter = clusterState.nodes().getNodes().valuesIt(); - while (nodesIter.hasNext()) { - final DiscoveryNode node = nodesIter.next(); - // check if the node is part of node decommissioned list - if (nodesToBeDecommissioned.contains(node)) { - return false; - } - } - return true; + Set intersection = Arrays.stream( + clusterState.nodes().getNodes().values().toArray(DiscoveryNode.class)).collect(Collectors.toSet()); + intersection.retainAll(nodesToBeDecommissioned); + return intersection.size() == 0; }; final ClusterStateObserver observer = new ClusterStateObserver(clusterService, timeout, logger, threadPool.getThreadContext()); @@ -187,14 +183,25 @@ public void onTimeout(TimeValue timeout) { }, allDecommissionedNodesRemovedPredicate); } - public void updateMetadataWithDecommissionStatus(DecommissionStatus decommissionStatus, ActionListener listener) { + public void updateMetadataWithDecommissionStatus(DecommissionStatus decommissionStatus, ActionListener listener) { clusterService.submitStateUpdateTask(decommissionStatus.status(), new ClusterStateUpdateTask(Priority.URGENT) { @Override - public ClusterState execute(ClusterState currentState) { + public ClusterState execute(ClusterState currentState) throws Exception { Metadata metadata = currentState.metadata(); DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.custom(DecommissionAttributeMetadata.TYPE); assert decommissionAttributeMetadata != null && decommissionAttributeMetadata.decommissionAttribute() != null; - assert assertStatusTransitionOrFailed(decommissionAttributeMetadata.status(), decommissionStatus); + // we need to update the status only when the previous stage is just behind than expected stage + // if the previous stage is already ahead of expected stage, we don't need to update the stage + // For failures, we update it no matter what + int previousStage = decommissionAttributeMetadata.status().stage(); + int expectedStage = decommissionStatus.stage(); + if (previousStage >= expectedStage) return currentState; + if (expectedStage - previousStage != 1 && !decommissionStatus.equals(DecommissionStatus.FAILED)) { + throw new DecommissioningFailedException( + decommissionAttributeMetadata.decommissionAttribute(), + "invalid previous decommission status found while updating status" + ); + } Metadata.Builder mdBuilder = Metadata.builder(metadata); DecommissionAttributeMetadata newMetadata = decommissionAttributeMetadata.withUpdatedStatus(decommissionStatus); mdBuilder.putCustom(DecommissionAttributeMetadata.TYPE, newMetadata); @@ -208,30 +215,10 @@ public void onFailure(String source, Exception e) { @Override public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { - DecommissionAttributeMetadata decommissionAttributeMetadata = newState.metadata() - .custom(DecommissionAttributeMetadata.TYPE); - assert decommissionAttributeMetadata.status().equals(decommissionStatus); - listener.onResponse(null); + DecommissionAttributeMetadata decommissionAttributeMetadata = newState.metadata().custom(DecommissionAttributeMetadata.TYPE); + logger.info("updated decommission status to [{}]", decommissionAttributeMetadata.status()); + listener.onResponse(decommissionAttributeMetadata.status()); } }); } - - private static boolean assertStatusTransitionOrFailed(DecommissionStatus oldStatus, DecommissionStatus newStatus) { - switch (newStatus) { - case DECOMMISSION_INIT: - // if the new status is INIT, then the old status cannot be anything but FAILED - return oldStatus.equals(DecommissionStatus.DECOMMISSION_FAILED); - case DECOMMISSION_IN_PROGRESS: - // if the new status is IN_PROGRESS, the old status has to be INIT - return oldStatus.equals(DecommissionStatus.DECOMMISSION_INIT); - case DECOMMISSION_SUCCESSFUL: - // if the new status is SUCCESSFUL, the old status has to be IN_PROGRESS - return oldStatus.equals(DecommissionStatus.DECOMMISSION_IN_PROGRESS); - case DECOMMISSION_FAILED: - // if the new status is FAILED, we don't need to assert for previous state - return true; - default: - throw new IllegalStateException("unexpected status [" + newStatus.status() + "] requested to update"); - } - } } diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 78fb555ac5546..680808d152335 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -48,11 +48,11 @@ * the service makes the best attempt to perform the following task - *

    *
  • Remove cluster-manager eligible nodes from voting config [TODO - checks to avoid quorum loss scenarios]
  • - *
  • Initiates nodes decommissioning by adding custom metadata with the attribute and state as {@link DecommissionStatus#DECOMMISSION_INIT}
  • - *
  • Triggers weigh away for nodes having given awareness attribute to drain. This marks the decommission status as {@link DecommissionStatus#DECOMMISSION_IN_PROGRESS}
  • + *
  • Initiates nodes decommissioning by adding custom metadata with the attribute and state as {@link DecommissionStatus#INIT}
  • + *
  • Triggers weigh away for nodes having given awareness attribute to drain. This marks the decommission status as {@link DecommissionStatus#IN_PROGRESS}
  • *
  • Once weighed away, the service triggers nodes decommission
  • - *
  • Once the decommission is successful, the service clears the voting config and marks the status as {@link DecommissionStatus#DECOMMISSION_SUCCESSFUL}
  • - *
  • If service fails at any step, it would mark the status as {@link DecommissionStatus#DECOMMISSION_FAILED}
  • + *
  • Once the decommission is successful, the service clears the voting config and marks the status as {@link DecommissionStatus#SUCCESSFUL}
  • + *
  • If service fails at any step, it would mark the status as {@link DecommissionStatus#FAILED}
  • *
* * @opensearch.internal @@ -107,118 +107,44 @@ private void setForcedAwarenessAttributes(Settings forceSettings) { this.forcedAwarenessAttributes = forcedAwarenessAttributes; } - public void initiateAttributeDecommissioning( - final DecommissionAttribute decommissionAttribute, - final ActionListener listener, - ClusterState state - ) { - // validates if the correct awareness attributes and forced awareness attribute set to the cluster before initiating decommission - // action - validateAwarenessAttribute(decommissionAttribute, awarenessAttributes, forcedAwarenessAttributes); - - Set clusterManagerNodesToBeDecommissioned = filterNodesWithDecommissionAttribute(state, decommissionAttribute, true); - ensureNoQuorumLossDueToDecommissioning( - decommissionAttribute, - clusterManagerNodesToBeDecommissioned, - state.getLastAcceptedConfiguration() - ); - - DecommissionAttributeMetadata decommissionAttributeMetadata = state.metadata().custom(DecommissionAttributeMetadata.TYPE); - // validates that there's no inflight decommissioning or already executed decommission in place - ensureNoInflightDifferentDecommissionRequest(decommissionAttributeMetadata, decommissionAttribute); - - logger.info("initiating awareness attribute [{}] decommissioning", decommissionAttribute.toString()); - - // remove all 'to-be-decommissioned' cluster manager eligible nodes from voting config - // The method ensures that we don't exclude same nodes multiple times - excludeDecommissionedClusterManagerNodesFromVotingConfig(clusterManagerNodesToBeDecommissioned); - - // explicitly throwing NotClusterManagerException as we can certainly say the local cluster manager node will - // be abdicated and soon will no longer be cluster manager. - if (transportService.getLocalNode().isClusterManagerNode() - && !nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute)) { - registerDecommissionAttribute(decommissionAttribute, listener); - } else { - throw new NotClusterManagerException( - "node [" - + transportService.getLocalNode().toString() - + "] not eligible to execute decommission request. Will retry until timeout." - ); - } - } - - private void excludeDecommissionedClusterManagerNodesFromVotingConfig(Set clusterManagerNodesToBeDecommissioned) { - Set clusterManagerNodesNameToBeDecommissioned = clusterManagerNodesToBeDecommissioned.stream() - .map(DiscoveryNode::getName) - .collect(Collectors.toSet()); - - Set currentVotingConfigExclusions = clusterService.getClusterApplierService() - .state() - .coordinationMetadata() - .getVotingConfigExclusions(); - Set excludedNodesName = currentVotingConfigExclusions.stream() - .map(VotingConfigExclusion::getNodeName) - .collect(Collectors.toSet()); - - // check if the to-be-excluded nodes are excluded. If yes, we don't need to exclude them again - if (clusterManagerNodesNameToBeDecommissioned.size() == 0 - || (clusterManagerNodesNameToBeDecommissioned.size() == excludedNodesName.size() - && excludedNodesName.containsAll(clusterManagerNodesNameToBeDecommissioned))) { - return; - } - // send a transport request to exclude to-be-decommissioned cluster manager eligible nodes from voting config - decommissionController.excludeDecommissionedNodesFromVotingConfig( - clusterManagerNodesNameToBeDecommissioned, - new ActionListener() { - @Override - public void onResponse(Void unused) { - logger.info( - "successfully removed decommissioned cluster manager eligible nodes [{}] from voting config ", - clusterManagerNodesToBeDecommissioned.toString() - ); - } - - @Override - public void onFailure(Exception e) { - logger.debug( - new ParameterizedMessage("failure in removing decommissioned cluster manager eligible nodes from voting config"), - e - ); - } - } - ); - } - /** - * Registers new decommissioned attribute metadata in the cluster state with {@link DecommissionStatus#DECOMMISSION_INIT} - *

- * This method can be only called on the cluster-manager node. It tries to create a new decommissioned attribute on the cluster manager - * and if it was successful it adds new decommissioned attribute to cluster metadata. - *

- * This method would only be executed on eligible cluster manager node + * Starts the new decommission request and registers the metadata with status as {@link DecommissionStatus#INIT} + * or the last known status if not {@link DecommissionStatus#FAILED} + * Once the status is updated, it tries to exclude to-be-decommissioned cluster manager nodes from Voting Configuration * * @param decommissionAttribute register decommission attribute in the metadata request - * @param listener register decommission listener + * @param listener register decommission listener */ - private void registerDecommissionAttribute( + public synchronized void startDecommissionAction( final DecommissionAttribute decommissionAttribute, final ActionListener listener ) { + // validates if correct awareness attributes and forced awareness attribute set to the cluster before starting action + validateAwarenessAttribute(decommissionAttribute, awarenessAttributes, forcedAwarenessAttributes); + + // register the metadata with status as DECOMMISSION_INIT as first step clusterService.submitStateUpdateTask( - "put_decommission [" + decommissionAttribute + "]", + "decommission [" + decommissionAttribute + "]", new ClusterStateUpdateTask(Priority.URGENT) { @Override - public ClusterState execute(ClusterState currentState) { + public ClusterState execute(ClusterState currentState) throws Exception { Metadata metadata = currentState.metadata(); Metadata.Builder mdBuilder = Metadata.builder(metadata); DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.custom(DecommissionAttributeMetadata.TYPE); + // check if the same attribute is requested for decommission and currently not FAILED, then return the current state as is + if(decommissionAttributeMetadata!=null && + decommissionAttributeMetadata.decommissionAttribute().equals(decommissionAttribute) && + !decommissionAttributeMetadata.status().equals(DecommissionStatus.FAILED)) { + logger.info("re-request received for decommissioning [{}], will not update state", decommissionAttribute); + return currentState; + } + // check the request sanity and reject the request if there's any inflight or successful request already present ensureNoInflightDifferentDecommissionRequest(decommissionAttributeMetadata, decommissionAttribute); decommissionAttributeMetadata = new DecommissionAttributeMetadata(decommissionAttribute); mdBuilder.putCustom(DecommissionAttributeMetadata.TYPE, decommissionAttributeMetadata); logger.info( - "registering decommission metadata for attribute [{}] with status as [{}]", - decommissionAttribute.toString(), - DecommissionStatus.DECOMMISSION_INIT + "registering decommission metadata [{}] to execute action", + decommissionAttributeMetadata.toString() ); return ClusterState.builder(currentState).metadata(mdBuilder).build(); } @@ -237,12 +163,12 @@ public void onFailure(String source, Exception e) { } else { logger.error( () -> new ParameterizedMessage( - "failed to initiate decommissioning for attribute [{}]", + "failed to start decommission action for attribute [{}]", decommissionAttribute.toString() ), e ); - failAndClearVotingConfigExclusion(listener, e); + listener.onFailure(e); } } @@ -251,53 +177,97 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS DecommissionAttributeMetadata decommissionAttributeMetadata = newState.metadata() .custom(DecommissionAttributeMetadata.TYPE); assert decommissionAttribute.equals(decommissionAttributeMetadata.decommissionAttribute()); - assert DecommissionStatus.DECOMMISSION_INIT.equals(decommissionAttributeMetadata.status()); listener.onResponse(new ClusterStateUpdateResponse(true)); - weighAwayForGracefulDecommission(); + if (!decommissionAttributeMetadata.status().equals(DecommissionStatus.SUCCESSFUL)) { + decommissionClusterManagerNodes(decommissionAttributeMetadata.decommissionAttribute()); + } } } ); } - private void failAndClearVotingConfigExclusion(final ActionListener listener, Exception e) { - decommissionController.clearVotingConfigExclusion(new ActionListener() { - @Override - public void onResponse(Void unused) { - logger.info("successfully cleared voting config exclusion after failing to execute decommission request"); - } + private void decommissionClusterManagerNodes(final DecommissionAttribute decommissionAttribute) { + decommissionController.updateMetadataWithDecommissionStatus( + DecommissionStatus.EXCLUDE_LEADER_FROM_VOTING_CONFIG, + new ActionListener() { + @Override + public void onResponse(DecommissionStatus status) { + ClusterState state = clusterService.getClusterApplierService().state(); + Set clusterManagerNodesToBeDecommissioned = filterNodesWithDecommissionAttribute( + state, decommissionAttribute, true + ); + ensureNoQuorumLossDueToDecommissioning( + decommissionAttribute, + clusterManagerNodesToBeDecommissioned, + state.getLastCommittedConfiguration() + ); + // remove all 'to-be-decommissioned' cluster manager eligible nodes from voting config + // The method ensures that we don't exclude same nodes multiple times + excludeDecommissionedClusterManagerNodesFromVotingConfig(clusterManagerNodesToBeDecommissioned); + // explicitly throwing NotClusterManagerException as we can certainly say the local cluster manager node will + // be abdicated and soon will no longer be cluster manager. + if (transportService.getLocalNode().isClusterManagerNode() + && !nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute)) { + failDecommissionedNodes(clusterService.getClusterApplierService().state()); + } else { + throw new NotClusterManagerException( + "node [" + + transportService.getLocalNode().toString() + + "] not eligible to execute decommission request. Will retry until timeout." + ); + } + } - @Override - public void onFailure(Exception e) { - logger.debug( - new ParameterizedMessage("failure in clearing voting config exclusion after failing to execute decommission request"), - e - ); + @Override + public void onFailure(Exception e) { + logger.error( + () -> new ParameterizedMessage( + "failed to update decommission status for attribute [{}] to [{}]", + decommissionAttribute.toString(), + DecommissionStatus.EXCLUDE_LEADER_FROM_VOTING_CONFIG + ), + e + ); + } } - }); - listener.onFailure(e); + ); } - private void weighAwayForGracefulDecommission() { - decommissionController.updateMetadataWithDecommissionStatus( - DecommissionStatus.DECOMMISSION_IN_PROGRESS, + private void excludeDecommissionedClusterManagerNodesFromVotingConfig(Set clusterManagerNodesToBeDecommissioned) { + Set clusterManagerNodesNameToBeDecommissioned = clusterManagerNodesToBeDecommissioned.stream() + .map(DiscoveryNode::getName) + .collect(Collectors.toSet()); + + Set currentVotingConfigExclusions = clusterService.getClusterApplierService() + .state() + .coordinationMetadata() + .getVotingConfigExclusions(); + Set excludedNodesName = currentVotingConfigExclusions.stream() + .map(VotingConfigExclusion::getNodeName) + .collect(Collectors.toSet()); + + // check if the to-be-excluded nodes are excluded. If yes, we don't need to exclude them again + if (clusterManagerNodesNameToBeDecommissioned.size() == 0 + || (clusterManagerNodesNameToBeDecommissioned.size() == excludedNodesName.size() + && excludedNodesName.containsAll(clusterManagerNodesNameToBeDecommissioned))) { + return; + } + // send a transport request to exclude to-be-decommissioned cluster manager eligible nodes from voting config + decommissionController.excludeDecommissionedNodesFromVotingConfig( + clusterManagerNodesNameToBeDecommissioned, new ActionListener() { @Override public void onResponse(Void unused) { logger.info( - "updated decommission status to [{}], weighing away awareness attribute for graceful shutdown", - DecommissionStatus.DECOMMISSION_IN_PROGRESS + "successfully removed decommissioned cluster manager eligible nodes [{}] from voting config ", + clusterManagerNodesToBeDecommissioned.toString() ); - // TODO - should trigger weigh away here and on successful weigh away -> fail the decommissioned nodes - failDecommissionedNodes(clusterService.getClusterApplierService().state()); } @Override public void onFailure(Exception e) { - logger.error( - () -> new ParameterizedMessage( - "failed to update decommission status to [{}], will not proceed with decommission", - DecommissionStatus.DECOMMISSION_IN_PROGRESS - ), + logger.debug( + new ParameterizedMessage("failure in removing decommissioned cluster manager eligible nodes from voting config"), e ); } @@ -307,23 +277,42 @@ public void onFailure(Exception e) { private void failDecommissionedNodes(ClusterState state) { DecommissionAttributeMetadata decommissionAttributeMetadata = state.metadata().custom(DecommissionAttributeMetadata.TYPE); - assert decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_IN_PROGRESS) - : "unexpected status encountered while decommissioning nodes"; DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute(); - - // execute nodes decommissioning - decommissionController.removeDecommissionedNodes( - filterNodesWithDecommissionAttribute(state, decommissionAttribute, false), - "nodes-decommissioned", - TimeValue.timeValueSeconds(30L), // TODO - read timeout from request while integrating with API - new ActionListener() { + decommissionController.updateMetadataWithDecommissionStatus( + DecommissionStatus.IN_PROGRESS, + new ActionListener() { @Override - public void onResponse(Void unused) { - clearVotingConfigExclusionAndUpdateStatus(true); + public void onResponse(DecommissionStatus status) { + // execute nodes decommissioning + decommissionController.removeDecommissionedNodes( + filterNodesWithDecommissionAttribute(state, decommissionAttribute, false), + "nodes-decommissioned", + TimeValue.timeValueSeconds(30L), // TODO - read timeout from request while integrating with API + new ActionListener() { + @Override + public void onResponse(Void unused) { + clearVotingConfigExclusionAndUpdateStatus(true); + } + + @Override + public void onFailure(Exception e) { + clearVotingConfigExclusionAndUpdateStatus(false); + } + } + ); } @Override public void onFailure(Exception e) { + logger.error( + () -> new ParameterizedMessage( + "failed to update decommission status for attribute [{}] to [{}]", + decommissionAttribute.toString(), + DecommissionStatus.IN_PROGRESS + ), + e + ); + // since we are not able to update the status, we will clear the voting config exclusion we have set earlier clearVotingConfigExclusionAndUpdateStatus(false); } } @@ -331,18 +320,15 @@ public void onFailure(Exception e) { } private void clearVotingConfigExclusionAndUpdateStatus(boolean decommissionSuccessful) { - ActionListener statusUpdateListener = new ActionListener() { + ActionListener statusUpdateListener = new ActionListener() { @Override - public void onResponse(Void unused) { - logger.info( - "successful updated decommission status with [{}]", - decommissionSuccessful ? DecommissionStatus.DECOMMISSION_SUCCESSFUL : DecommissionStatus.DECOMMISSION_FAILED - ); + public void onResponse(DecommissionStatus status) { + logger.info("completed decommission action"); } @Override public void onFailure(Exception e) { - logger.error("failed to update the decommission status"); + logger.error("failure encountered while executing decommission action"); } }; decommissionController.clearVotingConfigExclusion(new ActionListener() { @@ -350,8 +336,8 @@ public void onFailure(Exception e) { public void onResponse(Void unused) { logger.info("successfully cleared voting config exclusion after failing to execute decommission request"); DecommissionStatus updateStatusWith = decommissionSuccessful - ? DecommissionStatus.DECOMMISSION_SUCCESSFUL - : DecommissionStatus.DECOMMISSION_FAILED; + ? DecommissionStatus.SUCCESSFUL + : DecommissionStatus.FAILED; decommissionController.updateMetadataWithDecommissionStatus(updateStatusWith, statusUpdateListener); } @@ -361,7 +347,7 @@ public void onFailure(Exception e) { new ParameterizedMessage("failure in clearing voting config exclusion after processing decommission request"), e ); - decommissionController.updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_FAILED, statusUpdateListener); + decommissionController.updateMetadataWithDecommissionStatus(DecommissionStatus.FAILED, statusUpdateListener); } }); } @@ -418,10 +404,10 @@ private static void ensureNoInflightDifferentDecommissionRequest( ) { String msg = null; if (decommissionAttributeMetadata != null) { - if (decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_SUCCESSFUL)) { + if (decommissionAttributeMetadata.status().equals(DecommissionStatus.SUCCESSFUL)) { // one awareness attribute is already decommissioned. We will reject the new request msg = "one awareness attribute already successfully decommissioned. Recommission before triggering another decommission"; - } else if (decommissionAttributeMetadata.status().equals(DecommissionStatus.DECOMMISSION_FAILED)) { + } else if (decommissionAttributeMetadata.status().equals(DecommissionStatus.FAILED)) { // here we are sure that the previous decommission request failed, we can let this request pass this check return; } else { diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java index 1474faa9bb227..567ba091b1392 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java @@ -15,24 +15,30 @@ public enum DecommissionStatus { /** * Decommission process is initiated */ - DECOMMISSION_INIT("decommission_init"), + INIT("init", 0), /** - * Decommission process has started, decommissioned nodes should be weighed away + * Exclude cluster manager from Voting Configuration */ - DECOMMISSION_IN_PROGRESS("decommission_in_progress"), + EXCLUDE_LEADER_FROM_VOTING_CONFIG("exclude_leader_from_voting_config", 2), /** - * Decommissioning awareness attribute completed + * Decommission process has started, decommissioned nodes should be removed */ - DECOMMISSION_SUCCESSFUL("decommission_successful"), + IN_PROGRESS("in_progress", 3), + /** + * Decommission action completed + */ + SUCCESSFUL("successful", 4), /** * Decommission request failed */ - DECOMMISSION_FAILED("decommission_failed"); + FAILED("failed", -1); private final String status; + private final int stage; - DecommissionStatus(String status) { + DecommissionStatus(String status, int stage) { this.status = status; + this.stage = stage; } /** @@ -44,6 +50,13 @@ public String status() { return status; } + /** + * Returns stage that represents the decommission stage + */ + public int stage() { + return stage; + } + /** * Generate decommission status from given string * @@ -54,15 +67,38 @@ public static DecommissionStatus fromString(String status) { if (status == null) { throw new IllegalArgumentException("decommission status cannot be null"); } - if (status.equals(DECOMMISSION_INIT.status())) { - return DECOMMISSION_INIT; - } else if (status.equals(DECOMMISSION_IN_PROGRESS.status())) { - return DECOMMISSION_IN_PROGRESS; - } else if (status.equals(DECOMMISSION_SUCCESSFUL.status())) { - return DECOMMISSION_SUCCESSFUL; - } else if (status.equals(DECOMMISSION_FAILED.status())) { - return DECOMMISSION_FAILED; + if (status.equals(INIT.status())) { + return INIT; + } else if (status.equals(EXCLUDE_LEADER_FROM_VOTING_CONFIG.status())) { + return EXCLUDE_LEADER_FROM_VOTING_CONFIG; + } else if (status.equals(IN_PROGRESS.status())) { + return IN_PROGRESS; + } else if (status.equals(SUCCESSFUL.status())) { + return SUCCESSFUL; + } else if (status.equals(FAILED.status())) { + return FAILED; } throw new IllegalStateException("Decommission status [" + status + "] not recognized."); } + + /** + * Generate decommission status from given stage + * + * @param stage stage in int + * @return status + */ + public static DecommissionStatus fromStage(int stage) { + if (stage == INIT.stage()) { + return INIT; + } else if (stage == EXCLUDE_LEADER_FROM_VOTING_CONFIG.stage()) { + return EXCLUDE_LEADER_FROM_VOTING_CONFIG; + } else if (stage == IN_PROGRESS.stage()) { + return IN_PROGRESS; + } else if (stage == SUCCESSFUL.stage()) { + return SUCCESSFUL; + } else if (stage == FAILED.stage()) { + return FAILED; + } + throw new IllegalStateException("Decommission stage [" + stage + "] not recognized."); + } } diff --git a/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java b/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java index e9ad305637f95..97862656a2f4e 100644 --- a/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java +++ b/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java @@ -236,8 +236,8 @@ public void testPreventJoinClusterWithDecommission() { Settings.builder().build(); DecommissionAttribute decommissionAttribute = new DecommissionAttribute("zone", "zone-1"); DecommissionStatus decommissionStatus = randomFrom( - DecommissionStatus.DECOMMISSION_IN_PROGRESS, - DecommissionStatus.DECOMMISSION_SUCCESSFUL + DecommissionStatus.IN_PROGRESS, + DecommissionStatus.SUCCESSFUL ); DecommissionAttributeMetadata decommissionAttributeMetadata = new DecommissionAttributeMetadata( decommissionAttribute, @@ -270,7 +270,7 @@ public void testJoinClusterWithDifferentDecommission() { public void testJoinClusterWithDecommissionFailedOrInitOrRecommission() { Settings.builder().build(); DecommissionAttribute decommissionAttribute = new DecommissionAttribute("zone", "zone-1"); - DecommissionStatus decommissionStatus = randomFrom(DecommissionStatus.DECOMMISSION_INIT, DecommissionStatus.DECOMMISSION_FAILED); + DecommissionStatus decommissionStatus = randomFrom(DecommissionStatus.INIT, DecommissionStatus.FAILED); DecommissionAttributeMetadata decommissionAttributeMetadata = new DecommissionAttributeMetadata( decommissionAttribute, decommissionStatus diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java index 8e5d4e61937a4..ff27c39b9226b 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java @@ -237,7 +237,7 @@ public void testSuccessfulDecommissionStatusMetadataUpdate() throws InterruptedE final CountDownLatch countDownLatch = new CountDownLatch(1); DecommissionAttributeMetadata oldMetadata = new DecommissionAttributeMetadata( new DecommissionAttribute("zone", "zone-1"), - DecommissionStatus.DECOMMISSION_IN_PROGRESS + DecommissionStatus.IN_PROGRESS ); ClusterState state = clusterService.state(); Metadata metadata = state.metadata(); @@ -246,9 +246,9 @@ public void testSuccessfulDecommissionStatusMetadataUpdate() throws InterruptedE state = ClusterState.builder(state).metadata(mdBuilder).build(); setState(clusterService, state); - decommissionController.updateMetadataWithDecommissionStatus(DecommissionStatus.DECOMMISSION_SUCCESSFUL, new ActionListener() { + decommissionController.updateMetadataWithDecommissionStatus(DecommissionStatus.SUCCESSFUL, new ActionListener() { @Override - public void onResponse(Void unused) { + public void onResponse(DecommissionStatus status) { countDownLatch.countDown(); } @@ -260,7 +260,7 @@ public void onFailure(Exception e) { assertTrue(countDownLatch.await(30, TimeUnit.SECONDS)); ClusterState newState = clusterService.getClusterApplierService().state(); DecommissionAttributeMetadata decommissionAttributeMetadata = newState.metadata().custom(DecommissionAttributeMetadata.TYPE); - assertEquals(decommissionAttributeMetadata.status(), DecommissionStatus.DECOMMISSION_SUCCESSFUL); + assertEquals(decommissionAttributeMetadata.status(), DecommissionStatus.SUCCESSFUL); } private ClusterState addNodes(ClusterState clusterState, String zone, String... nodeIds) { diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java index df148c6201d3c..13af5203da3ca 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java @@ -146,7 +146,7 @@ public void testDecommissioningNotInitiatedForInvalidAttributeValue() { public void testDecommissioningNotInitiatedWhenAlreadyDecommissioned() { DecommissionAttributeMetadata oldMetadata = new DecommissionAttributeMetadata( new DecommissionAttribute("zone", "zone_1"), - DecommissionStatus.DECOMMISSION_IN_PROGRESS + DecommissionStatus.IN_PROGRESS ); final ClusterState.Builder builder = builder(clusterService.state()); setState( From a6f022a6cc60e7510d3a368466c6a95a6c10b609 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Wed, 7 Sep 2022 21:07:51 +0530 Subject: [PATCH 164/187] Trigger exclusion after init Signed-off-by: Rishab Nahata --- .../decommission/DecommissionService.java | 78 ++++++++----------- .../decommission/DecommissionStatus.java | 16 +--- 2 files changed, 36 insertions(+), 58 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 680808d152335..21aab2fe0b38d 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -177,60 +177,46 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS DecommissionAttributeMetadata decommissionAttributeMetadata = newState.metadata() .custom(DecommissionAttributeMetadata.TYPE); assert decommissionAttribute.equals(decommissionAttributeMetadata.decommissionAttribute()); - listener.onResponse(new ClusterStateUpdateResponse(true)); if (!decommissionAttributeMetadata.status().equals(DecommissionStatus.SUCCESSFUL)) { - decommissionClusterManagerNodes(decommissionAttributeMetadata.decommissionAttribute()); + decommissionClusterManagerNodes( + decommissionAttributeMetadata.decommissionAttribute(), + listener + ); } } } ); } - private void decommissionClusterManagerNodes(final DecommissionAttribute decommissionAttribute) { - decommissionController.updateMetadataWithDecommissionStatus( - DecommissionStatus.EXCLUDE_LEADER_FROM_VOTING_CONFIG, - new ActionListener() { - @Override - public void onResponse(DecommissionStatus status) { - ClusterState state = clusterService.getClusterApplierService().state(); - Set clusterManagerNodesToBeDecommissioned = filterNodesWithDecommissionAttribute( - state, decommissionAttribute, true - ); - ensureNoQuorumLossDueToDecommissioning( - decommissionAttribute, - clusterManagerNodesToBeDecommissioned, - state.getLastCommittedConfiguration() - ); - // remove all 'to-be-decommissioned' cluster manager eligible nodes from voting config - // The method ensures that we don't exclude same nodes multiple times - excludeDecommissionedClusterManagerNodesFromVotingConfig(clusterManagerNodesToBeDecommissioned); - // explicitly throwing NotClusterManagerException as we can certainly say the local cluster manager node will - // be abdicated and soon will no longer be cluster manager. - if (transportService.getLocalNode().isClusterManagerNode() - && !nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute)) { - failDecommissionedNodes(clusterService.getClusterApplierService().state()); - } else { - throw new NotClusterManagerException( - "node [" - + transportService.getLocalNode().toString() - + "] not eligible to execute decommission request. Will retry until timeout." - ); - } - } - - @Override - public void onFailure(Exception e) { - logger.error( - () -> new ParameterizedMessage( - "failed to update decommission status for attribute [{}] to [{}]", - decommissionAttribute.toString(), - DecommissionStatus.EXCLUDE_LEADER_FROM_VOTING_CONFIG - ), - e - ); - } - } + private void decommissionClusterManagerNodes( + final DecommissionAttribute decommissionAttribute, + ActionListener listener + ) { + ClusterState state = clusterService.getClusterApplierService().state(); + Set clusterManagerNodesToBeDecommissioned = filterNodesWithDecommissionAttribute( + state, decommissionAttribute, true + ); + ensureNoQuorumLossDueToDecommissioning( + decommissionAttribute, + clusterManagerNodesToBeDecommissioned, + state.getLastCommittedConfiguration() ); + // remove all 'to-be-decommissioned' cluster manager eligible nodes from voting config + // The method ensures that we don't exclude same nodes multiple times + excludeDecommissionedClusterManagerNodesFromVotingConfig(clusterManagerNodesToBeDecommissioned); + // explicitly throwing NotClusterManagerException as we can certainly say the local cluster manager node will + // be abdicated and soon will no longer be cluster manager. + if (transportService.getLocalNode().isClusterManagerNode() + && !nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute)) { + // we are good here to send the response now as the request is processed by an eligible active leader + listener.onResponse(new ClusterStateUpdateResponse(true)); + failDecommissionedNodes(clusterService.getClusterApplierService().state()); + } else { + // this will ensure that request is retried until cluster manager times out + listener.onFailure(new NotClusterManagerException("node [" + + transportService.getLocalNode().toString() + + "] not eligible to execute decommission request. Will retry until timeout.")); + } } private void excludeDecommissionedClusterManagerNodesFromVotingConfig(Set clusterManagerNodesToBeDecommissioned) { diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java index 567ba091b1392..ba3dec4ded94a 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java @@ -9,25 +9,21 @@ package org.opensearch.cluster.decommission; /** - * An enumeration of the states during decommissioning and recommissioning. + * An enumeration of the states during decommissioning */ public enum DecommissionStatus { /** - * Decommission process is initiated + * Decommission process is initiated, and to-be-decommissioned leader is excluded from voting config */ INIT("init", 0), - /** - * Exclude cluster manager from Voting Configuration - */ - EXCLUDE_LEADER_FROM_VOTING_CONFIG("exclude_leader_from_voting_config", 2), /** * Decommission process has started, decommissioned nodes should be removed */ - IN_PROGRESS("in_progress", 3), + IN_PROGRESS("in_progress", 1), /** * Decommission action completed */ - SUCCESSFUL("successful", 4), + SUCCESSFUL("successful", 2), /** * Decommission request failed */ @@ -69,8 +65,6 @@ public static DecommissionStatus fromString(String status) { } if (status.equals(INIT.status())) { return INIT; - } else if (status.equals(EXCLUDE_LEADER_FROM_VOTING_CONFIG.status())) { - return EXCLUDE_LEADER_FROM_VOTING_CONFIG; } else if (status.equals(IN_PROGRESS.status())) { return IN_PROGRESS; } else if (status.equals(SUCCESSFUL.status())) { @@ -90,8 +84,6 @@ public static DecommissionStatus fromString(String status) { public static DecommissionStatus fromStage(int stage) { if (stage == INIT.stage()) { return INIT; - } else if (stage == EXCLUDE_LEADER_FROM_VOTING_CONFIG.stage()) { - return EXCLUDE_LEADER_FROM_VOTING_CONFIG; } else if (stage == IN_PROGRESS.stage()) { return IN_PROGRESS; } else if (stage == SUCCESSFUL.stage()) { From cb3644de98feec2a3191e64c4bb312f2b5c58b21 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Wed, 7 Sep 2022 23:13:44 +0530 Subject: [PATCH 165/187] Updates Signed-off-by: Rishab Nahata --- .../decommission/DecommissionService.java | 64 ++++++++----------- 1 file changed, 28 insertions(+), 36 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 21aab2fe0b38d..58d068b12f1cd 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -47,8 +47,8 @@ * Whenever a cluster manager initiates operation to decommission an awareness attribute, * the service makes the best attempt to perform the following task - *

    - *
  • Remove cluster-manager eligible nodes from voting config [TODO - checks to avoid quorum loss scenarios]
  • *
  • Initiates nodes decommissioning by adding custom metadata with the attribute and state as {@link DecommissionStatus#INIT}
  • + *
  • Remove cluster-manager eligible nodes from voting config
  • *
  • Triggers weigh away for nodes having given awareness attribute to drain. This marks the decommission status as {@link DecommissionStatus#IN_PROGRESS}
  • *
  • Once weighed away, the service triggers nodes decommission
  • *
  • Once the decommission is successful, the service clears the voting config and marks the status as {@link DecommissionStatus#SUCCESSFUL}
  • @@ -151,25 +151,14 @@ public ClusterState execute(ClusterState currentState) throws Exception { @Override public void onFailure(String source, Exception e) { - if (e instanceof NotClusterManagerException) { - logger.debug( - () -> new ParameterizedMessage( - "cluster-manager updated while executing request for decommission attribute [{}]", - decommissionAttribute.toString() - ), - e - ); - // we don't want to send the failure response to the listener here as the request will be retried - } else { - logger.error( - () -> new ParameterizedMessage( - "failed to start decommission action for attribute [{}]", - decommissionAttribute.toString() - ), - e - ); - listener.onFailure(e); - } + logger.error( + () -> new ParameterizedMessage( + "failed to start decommission action for attribute [{}]", + decommissionAttribute.toString() + ), + e + ); + listener.onFailure(e); } @Override @@ -177,12 +166,10 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS DecommissionAttributeMetadata decommissionAttributeMetadata = newState.metadata() .custom(DecommissionAttributeMetadata.TYPE); assert decommissionAttribute.equals(decommissionAttributeMetadata.decommissionAttribute()); - if (!decommissionAttributeMetadata.status().equals(DecommissionStatus.SUCCESSFUL)) { - decommissionClusterManagerNodes( - decommissionAttributeMetadata.decommissionAttribute(), - listener - ); - } + decommissionClusterManagerNodes( + decommissionAttributeMetadata.decommissionAttribute(), + listener + ); } } ); @@ -196,15 +183,17 @@ private void decommissionClusterManagerNodes( Set clusterManagerNodesToBeDecommissioned = filterNodesWithDecommissionAttribute( state, decommissionAttribute, true ); - ensureNoQuorumLossDueToDecommissioning( - decommissionAttribute, - clusterManagerNodesToBeDecommissioned, - state.getLastCommittedConfiguration() - ); + try { + // this is a sanity check that the cluster will not go into a quorum loss state because of exclusion + ensureNoQuorumLossDueToDecommissioning(decommissionAttribute, clusterManagerNodesToBeDecommissioned, state.getLastCommittedConfiguration()); + } catch (DecommissioningFailedException dfe) { + listener.onFailure(dfe); + } // remove all 'to-be-decommissioned' cluster manager eligible nodes from voting config // The method ensures that we don't exclude same nodes multiple times excludeDecommissionedClusterManagerNodesFromVotingConfig(clusterManagerNodesToBeDecommissioned); - // explicitly throwing NotClusterManagerException as we can certainly say the local cluster manager node will + + // explicitly calling listener.onFailure with NotClusterManagerException as we can certainly say the local cluster manager node will // be abdicated and soon will no longer be cluster manager. if (transportService.getLocalNode().isClusterManagerNode() && !nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute)) { @@ -262,16 +251,19 @@ public void onFailure(Exception e) { } private void failDecommissionedNodes(ClusterState state) { + // this method ensures no matter what, we always exit from this function after clearing the voting config exclusion DecommissionAttributeMetadata decommissionAttributeMetadata = state.metadata().custom(DecommissionAttributeMetadata.TYPE); DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute(); decommissionController.updateMetadataWithDecommissionStatus( DecommissionStatus.IN_PROGRESS, - new ActionListener() { + new ActionListener<>() { @Override public void onResponse(DecommissionStatus status) { // execute nodes decommissioning decommissionController.removeDecommissionedNodes( - filterNodesWithDecommissionAttribute(state, decommissionAttribute, false), + filterNodesWithDecommissionAttribute( + clusterService.getClusterApplierService().state(), decommissionAttribute, false + ), "nodes-decommissioned", TimeValue.timeValueSeconds(30L), // TODO - read timeout from request while integrating with API new ActionListener() { @@ -306,7 +298,7 @@ public void onFailure(Exception e) { } private void clearVotingConfigExclusionAndUpdateStatus(boolean decommissionSuccessful) { - ActionListener statusUpdateListener = new ActionListener() { + ActionListener statusUpdateListener = new ActionListener<>() { @Override public void onResponse(DecommissionStatus status) { logger.info("completed decommission action"); @@ -320,7 +312,7 @@ public void onFailure(Exception e) { decommissionController.clearVotingConfigExclusion(new ActionListener() { @Override public void onResponse(Void unused) { - logger.info("successfully cleared voting config exclusion after failing to execute decommission request"); + logger.info("successfully cleared voting config exclusion after completing decommission action, proceeding to update metadata"); DecommissionStatus updateStatusWith = decommissionSuccessful ? DecommissionStatus.SUCCESSFUL : DecommissionStatus.FAILED; From 9ecf0ace8b687c2a0de98bf7d34e7ef7690b8013 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Thu, 8 Sep 2022 00:25:28 +0530 Subject: [PATCH 166/187] Resolving comments Signed-off-by: Rishab Nahata --- .../AddVotingConfigExclusionsRequest.java | 2 +- .../coordination/JoinTaskExecutor.java | 6 ++-- .../DecommissionAttributeMetadata.java | 2 +- .../decommission/DecommissionController.java | 28 ++++++++++++++++++ .../decommission/DecommissionService.java | 29 +++++++++++++++---- 5 files changed, 56 insertions(+), 11 deletions(-) diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/configuration/AddVotingConfigExclusionsRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/configuration/AddVotingConfigExclusionsRequest.java index 739bfaf2a3fb1..a2a77a1316898 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/configuration/AddVotingConfigExclusionsRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/configuration/AddVotingConfigExclusionsRequest.java @@ -157,7 +157,7 @@ Set resolveVotingConfigExclusions(ClusterState currentSta } else { assert nodeNames.length >= 1; Map existingNodes = StreamSupport.stream(allNodes.spliterator(), false) - .collect(Collectors.toMap(DiscoveryNode::getName, Function.identity(), (r1, r2) -> r1)); + .collect(Collectors.toMap(DiscoveryNode::getName, Function.identity())); for (String nodeName : nodeNames) { if (existingNodes.containsKey(nodeName)) { diff --git a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java index ee2b35eac302d..1c2a9466f76e3 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java @@ -482,10 +482,10 @@ public static void ensureNodeCommissioned(DiscoveryNode node, Metadata metadata) DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute(); DecommissionStatus status = decommissionAttributeMetadata.status(); if (decommissionAttribute != null && status != null) { - // We will let the node join the cluster if the current status is not IN_PROGRESS or SUCCESSFUL + // We will let the node join the cluster if the current status is FAILED if (node.getAttributes().get(decommissionAttribute.attributeName()).equals(decommissionAttribute.attributeValue()) - && (status.equals(DecommissionStatus.IN_PROGRESS) - || status.equals(DecommissionStatus.SUCCESSFUL))) { + && !status.equals(DecommissionStatus.FAILED) + ) { throw new NodeDecommissionedException( "node [{}] has decommissioned attribute [{}].", node.toString(), diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttributeMetadata.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttributeMetadata.java index fc3f1841615d9..009161ce66fc6 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttributeMetadata.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttributeMetadata.java @@ -111,7 +111,7 @@ public String getWriteableName() { @Override public Version getMinimalSupportedVersion() { - return Version.V_2_3_0; + return Version.V_3_0_0; } public DecommissionAttributeMetadata(StreamInput in) throws IOException { diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java index 9a87a48af60b2..403570782bbd5 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java @@ -71,6 +71,12 @@ public class DecommissionController { this.threadPool = threadPool; } + /** + * Transport call to add nodes to voting config exclusion + * + * @param nodes set of nodes to be added to voting config exclusion list + * @param listener callback for response or failure + */ public void excludeDecommissionedNodesFromVotingConfig(Set nodes, ActionListener listener) { transportService.sendRequest( transportService.getLocalNode(), @@ -100,6 +106,11 @@ public AddVotingConfigExclusionsResponse read(StreamInput in) throws IOException ); } + /** + * Transport call to clear voting config exclusion + * + * @param listener callback for response or failure + */ public void clearVotingConfigExclusion(ActionListener listener) { final ClearVotingConfigExclusionsRequest clearVotingConfigExclusionsRequest = new ClearVotingConfigExclusionsRequest(); transportService.sendRequest( @@ -130,6 +141,16 @@ public ClearVotingConfigExclusionsResponse read(StreamInput in) throws IOExcepti ); } + /** + * This method triggers batch of tasks for nodes to be decommissioned using executor {@link NodeRemovalClusterStateTaskExecutor} + * Once the tasks are submitted, it waits for an expected cluster state to guarantee + * that the expected decommissioned nodes are removed from the cluster + * + * @param nodesToBeDecommissioned set of the node to be decommissioned + * @param reason reason of removal + * @param timeout timeout for the request + * @param nodesRemovedListener callback for the success or failure + */ public void removeDecommissionedNodes( Set nodesToBeDecommissioned, String reason, @@ -183,6 +204,13 @@ public void onTimeout(TimeValue timeout) { }, allDecommissionedNodesRemovedPredicate); } + /** + * This method updates the status in the currently registered metadata. + * This method also validates the status with its previous state before executing the request + * + * @param decommissionStatus status to update decommission metadata with + * @param listener listener for response and failure + */ public void updateMetadataWithDecommissionStatus(DecommissionStatus decommissionStatus, ActionListener listener) { clusterService.submitStateUpdateTask(decommissionStatus.status(), new ClusterStateUpdateTask(Priority.URGENT) { @Override diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 58d068b12f1cd..7642bce77a37a 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -13,6 +13,7 @@ import org.apache.logging.log4j.message.ParameterizedMessage; import org.opensearch.action.ActionListener; import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.ClusterStateObserver; import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.NotClusterManagerException; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; @@ -188,19 +189,33 @@ private void decommissionClusterManagerNodes( ensureNoQuorumLossDueToDecommissioning(decommissionAttribute, clusterManagerNodesToBeDecommissioned, state.getLastCommittedConfiguration()); } catch (DecommissioningFailedException dfe) { listener.onFailure(dfe); + decommissionController.updateMetadataWithDecommissionStatus(DecommissionStatus.FAILED, new ActionListener() { + @Override + public void onResponse(DecommissionStatus status) { + logger.info("updated the status to [{}], as cluster could have gone to quorum loss situation due to decommissioning", status.toString()); + } + + @Override + public void onFailure(Exception e) { + logger.error("unexpected error found while updating the status", e); + } + }); } // remove all 'to-be-decommissioned' cluster manager eligible nodes from voting config // The method ensures that we don't exclude same nodes multiple times - excludeDecommissionedClusterManagerNodesFromVotingConfig(clusterManagerNodesToBeDecommissioned); + boolean toBeDecommissionedClusterManagerNodesExcluded = excludeDecommissionedClusterManagerNodesFromVotingConfig(clusterManagerNodesToBeDecommissioned); - // explicitly calling listener.onFailure with NotClusterManagerException as we can certainly say the local cluster manager node will - // be abdicated and soon will no longer be cluster manager. if (transportService.getLocalNode().isClusterManagerNode() - && !nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute)) { + && !nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute) + && toBeDecommissionedClusterManagerNodesExcluded + ) { // we are good here to send the response now as the request is processed by an eligible active leader + // and to-be-decommissioned cluster manager is no more part of Voting Configuration listener.onResponse(new ClusterStateUpdateResponse(true)); failDecommissionedNodes(clusterService.getClusterApplierService().state()); } else { + // explicitly calling listener.onFailure with NotClusterManagerException as we can certainly say the local cluster manager node will + // be abdicated and soon will no longer be cluster manager. // this will ensure that request is retried until cluster manager times out listener.onFailure(new NotClusterManagerException("node [" + transportService.getLocalNode().toString() @@ -208,7 +223,7 @@ private void decommissionClusterManagerNodes( } } - private void excludeDecommissionedClusterManagerNodesFromVotingConfig(Set clusterManagerNodesToBeDecommissioned) { + private boolean excludeDecommissionedClusterManagerNodesFromVotingConfig(Set clusterManagerNodesToBeDecommissioned) { Set clusterManagerNodesNameToBeDecommissioned = clusterManagerNodesToBeDecommissioned.stream() .map(DiscoveryNode::getName) .collect(Collectors.toSet()); @@ -225,7 +240,7 @@ private void excludeDecommissionedClusterManagerNodesFromVotingConfig(Set Date: Thu, 8 Sep 2022 01:17:23 +0530 Subject: [PATCH 167/187] Fixes Signed-off-by: Rishab Nahata --- .../opensearch/cluster/coordination/JoinTaskExecutor.java | 6 +++--- .../cluster/decommission/DecommissionService.java | 8 +++----- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java index 1c2a9466f76e3..bceb28b3a40b2 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java @@ -482,10 +482,10 @@ public static void ensureNodeCommissioned(DiscoveryNode node, Metadata metadata) DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute(); DecommissionStatus status = decommissionAttributeMetadata.status(); if (decommissionAttribute != null && status != null) { - // We will let the node join the cluster if the current status is FAILED + // We will let the node join the cluster if the current status is not IN_PROGRESS or SUCCESSFUL if (node.getAttributes().get(decommissionAttribute.attributeName()).equals(decommissionAttribute.attributeValue()) - && !status.equals(DecommissionStatus.FAILED) - ) { + && (status.equals(DecommissionStatus.IN_PROGRESS) + || status.equals(DecommissionStatus.SUCCESSFUL))) { throw new NodeDecommissionedException( "node [{}] has decommissioned attribute [{}].", node.toString(), diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 7642bce77a37a..fbde87bddcfd7 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -132,6 +132,8 @@ public ClusterState execute(ClusterState currentState) throws Exception { Metadata metadata = currentState.metadata(); Metadata.Builder mdBuilder = Metadata.builder(metadata); DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.custom(DecommissionAttributeMetadata.TYPE); + // check the request sanity and reject the request if there's any inflight or successful request already present + ensureNoInflightDifferentDecommissionRequest(decommissionAttributeMetadata, decommissionAttribute); // check if the same attribute is requested for decommission and currently not FAILED, then return the current state as is if(decommissionAttributeMetadata!=null && decommissionAttributeMetadata.decommissionAttribute().equals(decommissionAttribute) && @@ -139,8 +141,6 @@ public ClusterState execute(ClusterState currentState) throws Exception { logger.info("re-request received for decommissioning [{}], will not update state", decommissionAttribute); return currentState; } - // check the request sanity and reject the request if there's any inflight or successful request already present - ensureNoInflightDifferentDecommissionRequest(decommissionAttributeMetadata, decommissionAttribute); decommissionAttributeMetadata = new DecommissionAttributeMetadata(decommissionAttribute); mdBuilder.putCustom(DecommissionAttributeMetadata.TYPE, decommissionAttributeMetadata); logger.info( @@ -237,9 +237,7 @@ private boolean excludeDecommissionedClusterManagerNodesFromVotingConfig(Set Date: Thu, 8 Sep 2022 01:33:36 +0530 Subject: [PATCH 168/187] Fix spotless check Signed-off-by: Rishab Nahata --- .../coordination/JoinTaskExecutor.java | 3 +- .../decommission/DecommissionController.java | 11 +- .../decommission/DecommissionService.java | 220 +++++++++--------- .../coordination/JoinTaskExecutorTests.java | 5 +- .../DecommissionControllerTests.java | 21 +- .../DecommissionServiceTests.java | 22 +- 6 files changed, 136 insertions(+), 146 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java index bceb28b3a40b2..7410efc9ab60f 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java @@ -484,8 +484,7 @@ public static void ensureNodeCommissioned(DiscoveryNode node, Metadata metadata) if (decommissionAttribute != null && status != null) { // We will let the node join the cluster if the current status is not IN_PROGRESS or SUCCESSFUL if (node.getAttributes().get(decommissionAttribute.attributeName()).equals(decommissionAttribute.attributeValue()) - && (status.equals(DecommissionStatus.IN_PROGRESS) - || status.equals(DecommissionStatus.SUCCESSFUL))) { + && (status.equals(DecommissionStatus.IN_PROGRESS) || status.equals(DecommissionStatus.SUCCESSFUL))) { throw new NodeDecommissionedException( "node [{}] has decommissioned attribute [{}].", node.toString(), diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java index 403570782bbd5..cff01e4e480a5 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java @@ -157,7 +157,9 @@ public void removeDecommissionedNodes( TimeValue timeout, ActionListener nodesRemovedListener ) { - final Map nodesDecommissionTasks = new LinkedHashMap<>(nodesToBeDecommissioned.size()); + final Map nodesDecommissionTasks = new LinkedHashMap<>( + nodesToBeDecommissioned.size() + ); nodesToBeDecommissioned.forEach(discoveryNode -> { final NodeRemovalClusterStateTaskExecutor.Task task = new NodeRemovalClusterStateTaskExecutor.Task(discoveryNode, reason); nodesDecommissionTasks.put(task, nodeRemovalExecutor); @@ -170,8 +172,8 @@ public void removeDecommissionedNodes( ); Predicate allDecommissionedNodesRemovedPredicate = clusterState -> { - Set intersection = Arrays.stream( - clusterState.nodes().getNodes().values().toArray(DiscoveryNode.class)).collect(Collectors.toSet()); + Set intersection = Arrays.stream(clusterState.nodes().getNodes().values().toArray(DiscoveryNode.class)) + .collect(Collectors.toSet()); intersection.retainAll(nodesToBeDecommissioned); return intersection.size() == 0; }; @@ -243,7 +245,8 @@ public void onFailure(String source, Exception e) { @Override public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { - DecommissionAttributeMetadata decommissionAttributeMetadata = newState.metadata().custom(DecommissionAttributeMetadata.TYPE); + DecommissionAttributeMetadata decommissionAttributeMetadata = newState.metadata() + .custom(DecommissionAttributeMetadata.TYPE); logger.info("updated decommission status to [{}]", decommissionAttributeMetadata.status()); listener.onResponse(decommissionAttributeMetadata.status()); } diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index fbde87bddcfd7..50c7889400f95 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -13,7 +13,6 @@ import org.apache.logging.log4j.message.ParameterizedMessage; import org.opensearch.action.ActionListener; import org.opensearch.cluster.ClusterState; -import org.opensearch.cluster.ClusterStateObserver; import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.NotClusterManagerException; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; @@ -124,56 +123,47 @@ public synchronized void startDecommissionAction( validateAwarenessAttribute(decommissionAttribute, awarenessAttributes, forcedAwarenessAttributes); // register the metadata with status as DECOMMISSION_INIT as first step - clusterService.submitStateUpdateTask( - "decommission [" + decommissionAttribute + "]", - new ClusterStateUpdateTask(Priority.URGENT) { - @Override - public ClusterState execute(ClusterState currentState) throws Exception { - Metadata metadata = currentState.metadata(); - Metadata.Builder mdBuilder = Metadata.builder(metadata); - DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.custom(DecommissionAttributeMetadata.TYPE); - // check the request sanity and reject the request if there's any inflight or successful request already present - ensureNoInflightDifferentDecommissionRequest(decommissionAttributeMetadata, decommissionAttribute); - // check if the same attribute is requested for decommission and currently not FAILED, then return the current state as is - if(decommissionAttributeMetadata!=null && - decommissionAttributeMetadata.decommissionAttribute().equals(decommissionAttribute) && - !decommissionAttributeMetadata.status().equals(DecommissionStatus.FAILED)) { - logger.info("re-request received for decommissioning [{}], will not update state", decommissionAttribute); - return currentState; - } - decommissionAttributeMetadata = new DecommissionAttributeMetadata(decommissionAttribute); - mdBuilder.putCustom(DecommissionAttributeMetadata.TYPE, decommissionAttributeMetadata); - logger.info( - "registering decommission metadata [{}] to execute action", - decommissionAttributeMetadata.toString() - ); - return ClusterState.builder(currentState).metadata(mdBuilder).build(); + clusterService.submitStateUpdateTask("decommission [" + decommissionAttribute + "]", new ClusterStateUpdateTask(Priority.URGENT) { + @Override + public ClusterState execute(ClusterState currentState) throws Exception { + Metadata metadata = currentState.metadata(); + Metadata.Builder mdBuilder = Metadata.builder(metadata); + DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.custom(DecommissionAttributeMetadata.TYPE); + // check the request sanity and reject the request if there's any inflight or successful request already present + ensureNoInflightDifferentDecommissionRequest(decommissionAttributeMetadata, decommissionAttribute); + // check if the same attribute is requested for decommission and currently not FAILED, then return the current state as is + if (decommissionAttributeMetadata != null + && decommissionAttributeMetadata.decommissionAttribute().equals(decommissionAttribute) + && !decommissionAttributeMetadata.status().equals(DecommissionStatus.FAILED)) { + logger.info("re-request received for decommissioning [{}], will not update state", decommissionAttribute); + return currentState; } + decommissionAttributeMetadata = new DecommissionAttributeMetadata(decommissionAttribute); + mdBuilder.putCustom(DecommissionAttributeMetadata.TYPE, decommissionAttributeMetadata); + logger.info("registering decommission metadata [{}] to execute action", decommissionAttributeMetadata.toString()); + return ClusterState.builder(currentState).metadata(mdBuilder).build(); + } - @Override - public void onFailure(String source, Exception e) { - logger.error( - () -> new ParameterizedMessage( - "failed to start decommission action for attribute [{}]", - decommissionAttribute.toString() - ), - e - ); - listener.onFailure(e); - } + @Override + public void onFailure(String source, Exception e) { + logger.error( + () -> new ParameterizedMessage( + "failed to start decommission action for attribute [{}]", + decommissionAttribute.toString() + ), + e + ); + listener.onFailure(e); + } - @Override - public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { - DecommissionAttributeMetadata decommissionAttributeMetadata = newState.metadata() - .custom(DecommissionAttributeMetadata.TYPE); - assert decommissionAttribute.equals(decommissionAttributeMetadata.decommissionAttribute()); - decommissionClusterManagerNodes( - decommissionAttributeMetadata.decommissionAttribute(), - listener - ); - } + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + DecommissionAttributeMetadata decommissionAttributeMetadata = newState.metadata() + .custom(DecommissionAttributeMetadata.TYPE); + assert decommissionAttribute.equals(decommissionAttributeMetadata.decommissionAttribute()); + decommissionClusterManagerNodes(decommissionAttributeMetadata.decommissionAttribute(), listener); } - ); + }); } private void decommissionClusterManagerNodes( @@ -181,45 +171,59 @@ private void decommissionClusterManagerNodes( ActionListener listener ) { ClusterState state = clusterService.getClusterApplierService().state(); - Set clusterManagerNodesToBeDecommissioned = filterNodesWithDecommissionAttribute( - state, decommissionAttribute, true - ); + Set clusterManagerNodesToBeDecommissioned = filterNodesWithDecommissionAttribute(state, decommissionAttribute, true); try { // this is a sanity check that the cluster will not go into a quorum loss state because of exclusion - ensureNoQuorumLossDueToDecommissioning(decommissionAttribute, clusterManagerNodesToBeDecommissioned, state.getLastCommittedConfiguration()); + ensureNoQuorumLossDueToDecommissioning( + decommissionAttribute, + clusterManagerNodesToBeDecommissioned, + state.getLastCommittedConfiguration() + ); } catch (DecommissioningFailedException dfe) { listener.onFailure(dfe); - decommissionController.updateMetadataWithDecommissionStatus(DecommissionStatus.FAILED, new ActionListener() { - @Override - public void onResponse(DecommissionStatus status) { - logger.info("updated the status to [{}], as cluster could have gone to quorum loss situation due to decommissioning", status.toString()); - } + decommissionController.updateMetadataWithDecommissionStatus( + DecommissionStatus.FAILED, + new ActionListener() { + @Override + public void onResponse(DecommissionStatus status) { + logger.info( + "updated the status to [{}], as cluster could have gone to quorum loss situation due to decommissioning", + status.toString() + ); + } - @Override - public void onFailure(Exception e) { - logger.error("unexpected error found while updating the status", e); + @Override + public void onFailure(Exception e) { + logger.error("unexpected error found while updating the status", e); + } } - }); + ); } // remove all 'to-be-decommissioned' cluster manager eligible nodes from voting config // The method ensures that we don't exclude same nodes multiple times - boolean toBeDecommissionedClusterManagerNodesExcluded = excludeDecommissionedClusterManagerNodesFromVotingConfig(clusterManagerNodesToBeDecommissioned); + boolean toBeDecommissionedClusterManagerNodesExcluded = excludeDecommissionedClusterManagerNodesFromVotingConfig( + clusterManagerNodesToBeDecommissioned + ); if (transportService.getLocalNode().isClusterManagerNode() && !nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute) - && toBeDecommissionedClusterManagerNodesExcluded - ) { + && toBeDecommissionedClusterManagerNodesExcluded) { // we are good here to send the response now as the request is processed by an eligible active leader // and to-be-decommissioned cluster manager is no more part of Voting Configuration listener.onResponse(new ClusterStateUpdateResponse(true)); failDecommissionedNodes(clusterService.getClusterApplierService().state()); } else { - // explicitly calling listener.onFailure with NotClusterManagerException as we can certainly say the local cluster manager node will + // explicitly calling listener.onFailure with NotClusterManagerException as we can certainly say the local cluster manager node + // will // be abdicated and soon will no longer be cluster manager. // this will ensure that request is retried until cluster manager times out - listener.onFailure(new NotClusterManagerException("node [" - + transportService.getLocalNode().toString() - + "] not eligible to execute decommission request. Will retry until timeout.")); + listener.onFailure( + new NotClusterManagerException( + "node [" + + transportService.getLocalNode().toString() + + "] not eligible to execute decommission request. Will retry until timeout." + ) + ); } } @@ -237,7 +241,8 @@ private boolean excludeDecommissionedClusterManagerNodesFromVotingConfig(Set() { - @Override - public void onResponse(DecommissionStatus status) { - // execute nodes decommissioning - decommissionController.removeDecommissionedNodes( - filterNodesWithDecommissionAttribute( - clusterService.getClusterApplierService().state(), decommissionAttribute, false - ), - "nodes-decommissioned", - TimeValue.timeValueSeconds(30L), // TODO - read timeout from request while integrating with API - new ActionListener() { - @Override - public void onResponse(Void unused) { - clearVotingConfigExclusionAndUpdateStatus(true); - } - - @Override - public void onFailure(Exception e) { - clearVotingConfigExclusionAndUpdateStatus(false); - } + decommissionController.updateMetadataWithDecommissionStatus(DecommissionStatus.IN_PROGRESS, new ActionListener<>() { + @Override + public void onResponse(DecommissionStatus status) { + // execute nodes decommissioning + decommissionController.removeDecommissionedNodes( + filterNodesWithDecommissionAttribute(clusterService.getClusterApplierService().state(), decommissionAttribute, false), + "nodes-decommissioned", + TimeValue.timeValueSeconds(30L), // TODO - read timeout from request while integrating with API + new ActionListener() { + @Override + public void onResponse(Void unused) { + clearVotingConfigExclusionAndUpdateStatus(true); } - ); - } - @Override - public void onFailure(Exception e) { - logger.error( - () -> new ParameterizedMessage( - "failed to update decommission status for attribute [{}] to [{}]", - decommissionAttribute.toString(), - DecommissionStatus.IN_PROGRESS - ), - e - ); - // since we are not able to update the status, we will clear the voting config exclusion we have set earlier - clearVotingConfigExclusionAndUpdateStatus(false); - } + @Override + public void onFailure(Exception e) { + clearVotingConfigExclusionAndUpdateStatus(false); + } + } + ); } - ); + + @Override + public void onFailure(Exception e) { + logger.error( + () -> new ParameterizedMessage( + "failed to update decommission status for attribute [{}] to [{}]", + decommissionAttribute.toString(), + DecommissionStatus.IN_PROGRESS + ), + e + ); + // since we are not able to update the status, we will clear the voting config exclusion we have set earlier + clearVotingConfigExclusionAndUpdateStatus(false); + } + }); } private void clearVotingConfigExclusionAndUpdateStatus(boolean decommissionSuccessful) { @@ -327,10 +327,10 @@ public void onFailure(Exception e) { decommissionController.clearVotingConfigExclusion(new ActionListener() { @Override public void onResponse(Void unused) { - logger.info("successfully cleared voting config exclusion after completing decommission action, proceeding to update metadata"); - DecommissionStatus updateStatusWith = decommissionSuccessful - ? DecommissionStatus.SUCCESSFUL - : DecommissionStatus.FAILED; + logger.info( + "successfully cleared voting config exclusion after completing decommission action, proceeding to update metadata" + ); + DecommissionStatus updateStatusWith = decommissionSuccessful ? DecommissionStatus.SUCCESSFUL : DecommissionStatus.FAILED; decommissionController.updateMetadataWithDecommissionStatus(updateStatusWith, statusUpdateListener); } diff --git a/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java b/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java index 97862656a2f4e..a0c979f972a70 100644 --- a/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java +++ b/server/src/test/java/org/opensearch/cluster/coordination/JoinTaskExecutorTests.java @@ -235,10 +235,7 @@ public void testJoinClusterWithNoDecommission() { public void testPreventJoinClusterWithDecommission() { Settings.builder().build(); DecommissionAttribute decommissionAttribute = new DecommissionAttribute("zone", "zone-1"); - DecommissionStatus decommissionStatus = randomFrom( - DecommissionStatus.IN_PROGRESS, - DecommissionStatus.SUCCESSFUL - ); + DecommissionStatus decommissionStatus = randomFrom(DecommissionStatus.IN_PROGRESS, DecommissionStatus.SUCCESSFUL); DecommissionAttributeMetadata decommissionAttributeMetadata = new DecommissionAttributeMetadata( decommissionAttribute, decommissionStatus diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java index ff27c39b9226b..95199cdf09487 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java @@ -246,17 +246,20 @@ public void testSuccessfulDecommissionStatusMetadataUpdate() throws InterruptedE state = ClusterState.builder(state).metadata(mdBuilder).build(); setState(clusterService, state); - decommissionController.updateMetadataWithDecommissionStatus(DecommissionStatus.SUCCESSFUL, new ActionListener() { - @Override - public void onResponse(DecommissionStatus status) { - countDownLatch.countDown(); - } + decommissionController.updateMetadataWithDecommissionStatus( + DecommissionStatus.SUCCESSFUL, + new ActionListener() { + @Override + public void onResponse(DecommissionStatus status) { + countDownLatch.countDown(); + } - @Override - public void onFailure(Exception e) { - fail("decommission status update failed"); + @Override + public void onFailure(Exception e) { + fail("decommission status update failed"); + } } - }); + ); assertTrue(countDownLatch.await(30, TimeUnit.SECONDS)); ClusterState newState = clusterService.getClusterApplierService().state(); DecommissionAttributeMetadata decommissionAttributeMetadata = newState.metadata().custom(DecommissionAttributeMetadata.TYPE); diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java index 13af5203da3ca..63e285e2dab36 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java @@ -121,7 +121,7 @@ public void testDecommissioningNotInitiatedForInvalidAttributeName() { ActionListener listener = mock(ActionListener.class); DecommissioningFailedException e = expectThrows( DecommissioningFailedException.class, - () -> { decommissionService.initiateAttributeDecommissioning(decommissionAttribute, listener, clusterService.state()); } + () -> { decommissionService.startDecommissionAction(decommissionAttribute, listener); } ); assertThat(e.getMessage(), Matchers.endsWith("invalid awareness attribute requested for decommissioning")); } @@ -132,7 +132,7 @@ public void testDecommissioningNotInitiatedForInvalidAttributeValue() { ActionListener listener = mock(ActionListener.class); DecommissioningFailedException e = expectThrows( DecommissioningFailedException.class, - () -> { decommissionService.initiateAttributeDecommissioning(decommissionAttribute, listener, clusterService.state()); } + () -> { decommissionService.startDecommissionAction(decommissionAttribute, listener); } ); assertThat( e.getMessage(), @@ -158,13 +158,7 @@ public void testDecommissioningNotInitiatedWhenAlreadyDecommissioned() { ActionListener listener = mock(ActionListener.class); DecommissioningFailedException e = expectThrows( DecommissioningFailedException.class, - () -> { - decommissionService.initiateAttributeDecommissioning( - new DecommissionAttribute("zone", "zone_2"), - listener, - clusterService.state() - ); - } + () -> { decommissionService.startDecommissionAction(new DecommissionAttribute("zone", "zone_2"), listener); } ); assertThat(e.getMessage(), Matchers.endsWith("another request for decommission is in flight, will not process this request")); } @@ -172,7 +166,7 @@ public void testDecommissioningNotInitiatedWhenAlreadyDecommissioned() { @SuppressWarnings("unchecked") public void testDecommissioningInitiatedWhenEnoughClusterManagerNodes() { ActionListener listener = mock(ActionListener.class); - decommissionService.initiateAttributeDecommissioning(new DecommissionAttribute("zone", "zone_3"), listener, clusterService.state()); + decommissionService.startDecommissionAction(new DecommissionAttribute("zone", "zone_3"), listener); } @SuppressWarnings("unchecked") @@ -184,13 +178,7 @@ public void testDecommissioningNotInitiatedWhenNotEnoughClusterManagerNodes() { ActionListener listener = mock(ActionListener.class); DecommissioningFailedException e = expectThrows( DecommissioningFailedException.class, - () -> { - decommissionService.initiateAttributeDecommissioning( - new DecommissionAttribute("zone", "zone_3"), - listener, - clusterService.state() - ); - } + () -> { decommissionService.startDecommissionAction(new DecommissionAttribute("zone", "zone_3"), listener); } ); assertThat(e.getMessage(), Matchers.endsWith("cannot proceed with decommission request as cluster might go into quorum loss")); } From 19b77e7d711f84cd80237674f9ac076f2a3a7793 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Thu, 8 Sep 2022 11:46:54 +0530 Subject: [PATCH 169/187] Merge API and service --- .../get/GetDecommissionStateResponse.java | 8 +++++-- .../TransportGetDecommissionStateAction.java | 24 +++++++------------ .../put/TransportDecommissionAction.java | 15 ++++-------- .../decommission/DecommissionService.java | 7 +++--- .../DecommissionServiceTests.java | 11 +++++---- 5 files changed, 30 insertions(+), 35 deletions(-) diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionStateResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionStateResponse.java index 4b6c1f20db008..538dab44c6e4c 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionStateResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionStateResponse.java @@ -33,6 +33,10 @@ public class GetDecommissionStateResponse extends ActionResponse implements ToXC private final DecommissionAttribute decommissionedAttribute; private final DecommissionStatus status; + GetDecommissionStateResponse() { + this(null, null); + } + GetDecommissionStateResponse(DecommissionAttribute decommissionedAttribute, DecommissionStatus status) { this.decommissionedAttribute = decommissionedAttribute; this.status = status; @@ -40,13 +44,13 @@ public class GetDecommissionStateResponse extends ActionResponse implements ToXC GetDecommissionStateResponse(StreamInput in) throws IOException { this.decommissionedAttribute = new DecommissionAttribute(in); - this.status = DecommissionStatus.fromValue(in.readByte()); + this.status = DecommissionStatus.fromString(in.readString()); } @Override public void writeTo(StreamOutput out) throws IOException { decommissionedAttribute.writeTo(out); - out.writeByte(status.value()); + out.writeString(status.status()); } public DecommissionAttribute getDecommissionedAttribute() { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/TransportGetDecommissionStateAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/TransportGetDecommissionStateAction.java index ab3567d23cbdb..db8af85e37c07 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/TransportGetDecommissionStateAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/TransportGetDecommissionStateAction.java @@ -15,6 +15,7 @@ import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; import org.opensearch.cluster.decommission.DecommissionAttribute; +import org.opensearch.cluster.decommission.DecommissionAttributeMetadata; import org.opensearch.cluster.decommission.DecommissionStatus; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.metadata.Metadata; @@ -71,21 +72,14 @@ protected void clusterManagerOperation( ActionListener listener ) throws Exception { Metadata metadata = state.metadata(); - // DecommissionAttributeMetadata decommissionedAttributes = metadata.custom(DecommissionAttributeMetadata.TYPE); - // TODO - update once service layer changes are merged - // <<<<<<< HEAD - listener.onResponse( - new GetDecommissionStateResponse(new DecommissionAttribute("zone", "zone-1"), DecommissionStatus.DECOMMISSIONED) - ); - // ======= - // if (decommissionedAttributes!=null) { - // listener.onResponse(new GetDecommissionStateResponse(decommissionedAttributes.decommissionAttribute(), - // decommissionedAttributes.status())); - // } - // else { - // listener.onResponse(new GetDecommissionStateResponse()); - // } - // >>>>>>> 1025b6e3e3e (Fix GET without PUT) + DecommissionAttributeMetadata decommissionedAttributes = metadata.custom(DecommissionAttributeMetadata.TYPE); + if (decommissionedAttributes!=null) { + listener.onResponse(new GetDecommissionStateResponse(decommissionedAttributes.decommissionAttribute(), + decommissionedAttributes.status())); + } + else { + listener.onResponse(new GetDecommissionStateResponse()); + } } @Override diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/TransportDecommissionAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/TransportDecommissionAction.java index 166912c4a5e9c..4ed90e4fb13f9 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/TransportDecommissionAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/TransportDecommissionAction.java @@ -16,6 +16,7 @@ import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; +import org.opensearch.cluster.decommission.DecommissionService; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; @@ -33,12 +34,13 @@ public class TransportDecommissionAction extends TransportClusterManagerNodeAction { private static final Logger logger = LogManager.getLogger(TransportDecommissionAction.class); + private DecommissionService decommissionService; @Inject public TransportDecommissionAction( TransportService transportService, ClusterService clusterService, - // DecommissionService decommissionService, + DecommissionService decommissionService, ThreadPool threadPool, ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver @@ -52,8 +54,7 @@ public TransportDecommissionAction( DecommissionRequest::new, indexNameExpressionResolver ); - // TODO - uncomment when integrating with the service - // this.decommissionService = decommissionService; + this.decommissionService = decommissionService; } @Override @@ -75,12 +76,6 @@ protected ClusterBlockException checkBlock(DecommissionRequest request, ClusterS protected void clusterManagerOperation(DecommissionRequest request, ClusterState state, ActionListener listener) throws Exception { logger.info("initiating awareness attribute [{}] decommissioning", request.getDecommissionAttribute().toString()); - listener.onResponse(new DecommissionResponse(true)); // TODO - remove after integration - // TODO - uncomment when integrating with the service - // decommissionService.initiateAttributeDecommissioning( - // request.getDecommissionAttribute(), - // listener, - // state - // ); + decommissionService.startDecommissionAction(request.getDecommissionAttribute(), listener); } } diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 50c7889400f95..4caae60bf9e5c 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -12,6 +12,7 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; import org.opensearch.action.ActionListener; +import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionResponse; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.NotClusterManagerException; @@ -117,7 +118,7 @@ private void setForcedAwarenessAttributes(Settings forceSettings) { */ public synchronized void startDecommissionAction( final DecommissionAttribute decommissionAttribute, - final ActionListener listener + final ActionListener listener ) { // validates if correct awareness attributes and forced awareness attribute set to the cluster before starting action validateAwarenessAttribute(decommissionAttribute, awarenessAttributes, forcedAwarenessAttributes); @@ -168,7 +169,7 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS private void decommissionClusterManagerNodes( final DecommissionAttribute decommissionAttribute, - ActionListener listener + ActionListener listener ) { ClusterState state = clusterService.getClusterApplierService().state(); Set clusterManagerNodesToBeDecommissioned = filterNodesWithDecommissionAttribute(state, decommissionAttribute, true); @@ -210,7 +211,7 @@ public void onFailure(Exception e) { && toBeDecommissionedClusterManagerNodesExcluded) { // we are good here to send the response now as the request is processed by an eligible active leader // and to-be-decommissioned cluster manager is no more part of Voting Configuration - listener.onResponse(new ClusterStateUpdateResponse(true)); + listener.onResponse(new DecommissionResponse(true)); failDecommissionedNodes(clusterService.getClusterApplierService().state()); } else { // explicitly calling listener.onFailure with NotClusterManagerException as we can certainly say the local cluster manager node diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java index 63e285e2dab36..978fbdb549b4c 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java @@ -13,6 +13,7 @@ import org.junit.Before; import org.opensearch.Version; import org.opensearch.action.ActionListener; +import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionResponse; import org.opensearch.cluster.ClusterName; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; @@ -118,7 +119,7 @@ public void shutdownThreadPoolAndClusterService() { @SuppressWarnings("unchecked") public void testDecommissioningNotInitiatedForInvalidAttributeName() { DecommissionAttribute decommissionAttribute = new DecommissionAttribute("rack", "rack-a"); - ActionListener listener = mock(ActionListener.class); + ActionListener listener = mock(ActionListener.class); DecommissioningFailedException e = expectThrows( DecommissioningFailedException.class, () -> { decommissionService.startDecommissionAction(decommissionAttribute, listener); } @@ -129,7 +130,7 @@ public void testDecommissioningNotInitiatedForInvalidAttributeName() { @SuppressWarnings("unchecked") public void testDecommissioningNotInitiatedForInvalidAttributeValue() { DecommissionAttribute decommissionAttribute = new DecommissionAttribute("zone", "random"); - ActionListener listener = mock(ActionListener.class); + ActionListener listener = mock(ActionListener.class); DecommissioningFailedException e = expectThrows( DecommissioningFailedException.class, () -> { decommissionService.startDecommissionAction(decommissionAttribute, listener); } @@ -155,7 +156,7 @@ public void testDecommissioningNotInitiatedWhenAlreadyDecommissioned() { Metadata.builder(clusterService.state().metadata()).putCustom(DecommissionAttributeMetadata.TYPE, oldMetadata).build() ) ); - ActionListener listener = mock(ActionListener.class); + ActionListener listener = mock(ActionListener.class); DecommissioningFailedException e = expectThrows( DecommissioningFailedException.class, () -> { decommissionService.startDecommissionAction(new DecommissionAttribute("zone", "zone_2"), listener); } @@ -165,7 +166,7 @@ public void testDecommissioningNotInitiatedWhenAlreadyDecommissioned() { @SuppressWarnings("unchecked") public void testDecommissioningInitiatedWhenEnoughClusterManagerNodes() { - ActionListener listener = mock(ActionListener.class); + ActionListener listener = mock(ActionListener.class); decommissionService.startDecommissionAction(new DecommissionAttribute("zone", "zone_3"), listener); } @@ -175,7 +176,7 @@ public void testDecommissioningNotInitiatedWhenNotEnoughClusterManagerNodes() { // shrink voting config state = setNodesInVotingConfig(state, state.nodes().get("node1"), state.nodes().get("node11")); setState(clusterService, state); - ActionListener listener = mock(ActionListener.class); + ActionListener listener = mock(ActionListener.class); DecommissioningFailedException e = expectThrows( DecommissioningFailedException.class, () -> { decommissionService.startDecommissionAction(new DecommissionAttribute("zone", "zone_3"), listener); } From 7ff54724f54833ccedfbfdc5a46ba9e55aed030a Mon Sep 17 00:00:00 2001 From: pranikum <109206473+pranikum@users.noreply.github.com> Date: Tue, 6 Sep 2022 22:27:27 +0530 Subject: [PATCH 170/187] Merge recommission --- .../org/opensearch/action/ActionModule.java | 5 + .../delete/DeleteDecommissionAction.java | 20 ++++ .../delete/DeleteDecommissionRequest.java | 40 ++++++++ .../DeleteDecommissionRequestBuilder.java | 22 +++++ .../delete/DeleteDecommissionResponse.java | 32 +++++++ .../TransportDeleteDecommissionAction.java | 96 +++++++++++++++++++ .../opensearch/client/ClusterAdminClient.java | 15 +++ .../java/org/opensearch/client/Requests.java | 8 ++ .../client/support/AbstractClient.java | 21 ++++ .../decommission/DecommissionService.java | 34 +++++++ .../cluster/RestDeleteDecommissionAction.java | 41 ++++++++ 11 files changed, 334 insertions(+) create mode 100644 server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/DeleteDecommissionAction.java create mode 100644 server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/DeleteDecommissionRequest.java create mode 100644 server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/DeleteDecommissionRequestBuilder.java create mode 100644 server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/DeleteDecommissionResponse.java create mode 100644 server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/TransportDeleteDecommissionAction.java create mode 100644 server/src/main/java/org/opensearch/rest/action/admin/cluster/RestDeleteDecommissionAction.java diff --git a/server/src/main/java/org/opensearch/action/ActionModule.java b/server/src/main/java/org/opensearch/action/ActionModule.java index 1d19875267678..0ca5c1b0fc398 100644 --- a/server/src/main/java/org/opensearch/action/ActionModule.java +++ b/server/src/main/java/org/opensearch/action/ActionModule.java @@ -44,6 +44,8 @@ import org.opensearch.action.admin.cluster.decommission.awareness.get.TransportGetDecommissionStateAction; import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionAction; import org.opensearch.action.admin.cluster.decommission.awareness.put.TransportDecommissionAction; +import org.opensearch.action.admin.cluster.decommission.awareness.delete.DeleteDecommissionAction; +import org.opensearch.action.admin.cluster.decommission.awareness.delete.TransportDeleteDecommissionAction; import org.opensearch.action.admin.cluster.health.ClusterHealthAction; import org.opensearch.action.admin.cluster.health.TransportClusterHealthAction; import org.opensearch.action.admin.cluster.node.hotthreads.NodesHotThreadsAction; @@ -306,6 +308,7 @@ import org.opensearch.rest.action.admin.cluster.RestClusterStatsAction; import org.opensearch.rest.action.admin.cluster.RestClusterUpdateSettingsAction; import org.opensearch.rest.action.admin.cluster.RestCreateSnapshotAction; +import org.opensearch.rest.action.admin.cluster.RestDeleteDecommissionAction; import org.opensearch.rest.action.admin.cluster.RestDeleteRepositoryAction; import org.opensearch.rest.action.admin.cluster.RestDeleteSnapshotAction; import org.opensearch.rest.action.admin.cluster.RestDeleteStoredScriptAction; @@ -692,6 +695,7 @@ public void reg // Decommission actions actions.register(DecommissionAction.INSTANCE, TransportDecommissionAction.class); actions.register(GetDecommissionStateAction.INSTANCE, TransportGetDecommissionStateAction.class); + actions.register(DeleteDecommissionAction.INSTANCE, TransportDeleteDecommissionAction.class); return unmodifiableMap(actions.getRegistry()); } @@ -885,6 +889,7 @@ public void initRestHandlers(Supplier nodesInCluster) { registerHandler.accept(new RestCatAction(catActions)); registerHandler.accept(new RestDecommissionAction()); registerHandler.accept(new RestGetDecommissionStateAction()); + registerHandler.accept(new RestDeleteDecommissionAction()); // Remote Store APIs if (FeatureFlags.isEnabled(FeatureFlags.REMOTE_STORE)) { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/DeleteDecommissionAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/DeleteDecommissionAction.java new file mode 100644 index 0000000000000..4bb276197a912 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/DeleteDecommissionAction.java @@ -0,0 +1,20 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.decommission.awareness.delete; + +import org.opensearch.action.ActionType; + +public class DeleteDecommissionAction extends ActionType { + public static final DeleteDecommissionAction INSTANCE = new DeleteDecommissionAction(); + public static final String NAME = "cluster:admin/decommission/awareness/delete"; + + private DeleteDecommissionAction() { + super(NAME, DeleteDecommissionResponse::new); + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/DeleteDecommissionRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/DeleteDecommissionRequest.java new file mode 100644 index 0000000000000..8804895d9a6a1 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/DeleteDecommissionRequest.java @@ -0,0 +1,40 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.decommission.awareness.delete; + +import org.opensearch.action.ActionRequestValidationException; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeRequest; +import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.common.io.stream.StreamOutput; + +import java.io.IOException; + +public class DeleteDecommissionRequest extends ClusterManagerNodeRequest { + + public DeleteDecommissionRequest() {} + + public DeleteDecommissionRequest(StreamInput in) throws IOException { + super(in); + } + + @Override + public ActionRequestValidationException validate() { + return null; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + } + + @Override + public String toString() { + return "DeleteDecommissionRequest"; + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/DeleteDecommissionRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/DeleteDecommissionRequestBuilder.java new file mode 100644 index 0000000000000..50e282450e820 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/DeleteDecommissionRequestBuilder.java @@ -0,0 +1,22 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.decommission.awareness.delete; + +import org.opensearch.action.support.clustermanager.ClusterManagerNodeOperationRequestBuilder; +import org.opensearch.client.OpenSearchClient; + +public class DeleteDecommissionRequestBuilder extends ClusterManagerNodeOperationRequestBuilder< + DeleteDecommissionRequest, + DeleteDecommissionResponse, + DeleteDecommissionRequestBuilder> { + + public DeleteDecommissionRequestBuilder(OpenSearchClient client, DeleteDecommissionAction action) { + super(client, action, new DeleteDecommissionRequest()); + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/DeleteDecommissionResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/DeleteDecommissionResponse.java new file mode 100644 index 0000000000000..a523a025b51f6 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/DeleteDecommissionResponse.java @@ -0,0 +1,32 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.decommission.awareness.delete; + +import org.opensearch.action.support.master.AcknowledgedResponse; +import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.common.xcontent.ToXContentObject; + +import java.io.IOException; + +public class DeleteDecommissionResponse extends AcknowledgedResponse implements ToXContentObject { + + DeleteDecommissionResponse(StreamInput in) throws IOException { + super(in); + } + + DeleteDecommissionResponse(boolean acknowledged) { + super(acknowledged); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/TransportDeleteDecommissionAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/TransportDeleteDecommissionAction.java new file mode 100644 index 0000000000000..34860f09ef005 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/TransportDeleteDecommissionAction.java @@ -0,0 +1,96 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.decommission.awareness.delete; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.action.ActionListener; +import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.ack.ClusterStateUpdateResponse; +import org.opensearch.cluster.block.ClusterBlockException; +import org.opensearch.cluster.block.ClusterBlockLevel; +import org.opensearch.cluster.decommission.DecommissionService; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.inject.Inject; +import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportService; + +import java.io.IOException; + +public class TransportDeleteDecommissionAction extends TransportClusterManagerNodeAction< + DeleteDecommissionRequest, + DeleteDecommissionResponse> { + + private static final Logger logger = LogManager.getLogger(TransportDeleteDecommissionAction.class); + + DecommissionService decommissionService; + + @Inject + public TransportDeleteDecommissionAction( + TransportService transportService, + ClusterService clusterService, + DecommissionService decommissionService, + ThreadPool threadPool, + ActionFilters actionFilters, + IndexNameExpressionResolver indexNameExpressionResolver + ) { + super( + DeleteDecommissionAction.NAME, + transportService, + clusterService, + threadPool, + actionFilters, + DeleteDecommissionRequest::new, + indexNameExpressionResolver + ); + this.decommissionService = decommissionService; + + } + + @Override + protected String executor() { + return ThreadPool.Names.SAME; + } + + @Override + protected DeleteDecommissionResponse read(StreamInput in) throws IOException { + return new DeleteDecommissionResponse(in); + } + + @Override + protected ClusterBlockException checkBlock(DeleteDecommissionRequest request, ClusterState state) { + return state.blocks().globalBlockedException(ClusterBlockLevel.METADATA_WRITE); + } + + @Override + protected void masterOperation( + DeleteDecommissionRequest request, + ClusterState state, + ActionListener listener + ) { + // TODO: Enable when service class change is merged + logger.info("Received delete decommission Request"); + decommissionService.clearDecommissionStatus(new ActionListener() { + @Override + public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { + listener.onResponse(new DeleteDecommissionResponse(true)); + } + + @Override + public void onFailure(Exception e) { + logger.error("Recommission failed with exception " + e.getMessage()); + } + }); + + } +} diff --git a/server/src/main/java/org/opensearch/client/ClusterAdminClient.java b/server/src/main/java/org/opensearch/client/ClusterAdminClient.java index 69c63017632c1..550659788a8e1 100644 --- a/server/src/main/java/org/opensearch/client/ClusterAdminClient.java +++ b/server/src/main/java/org/opensearch/client/ClusterAdminClient.java @@ -43,6 +43,9 @@ import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionRequest; import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionRequestBuilder; import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionResponse; +import org.opensearch.action.admin.cluster.decommission.awareness.delete.DeleteDecommissionRequest; +import org.opensearch.action.admin.cluster.decommission.awareness.delete.DeleteDecommissionRequestBuilder; +import org.opensearch.action.admin.cluster.decommission.awareness.delete.DeleteDecommissionResponse; import org.opensearch.action.admin.cluster.health.ClusterHealthRequest; import org.opensearch.action.admin.cluster.health.ClusterHealthRequestBuilder; import org.opensearch.action.admin.cluster.health.ClusterHealthResponse; @@ -827,4 +830,16 @@ public interface ClusterAdminClient extends OpenSearchClient { * Get Decommissioned attribute */ GetDecommissionStateRequestBuilder prepareGetDecommission(); + + ActionFuture deleteDecommission(DeleteDecommissionRequest request); + + /** + * Decommission a node + */ + void deleteDecommission(DeleteDecommissionRequest request, ActionListener listener); + + /** + * Decommission a node + */ + DeleteDecommissionRequestBuilder prepareDeleteDecommission(); } diff --git a/server/src/main/java/org/opensearch/client/Requests.java b/server/src/main/java/org/opensearch/client/Requests.java index 38674f8d917cd..8acae2788843f 100644 --- a/server/src/main/java/org/opensearch/client/Requests.java +++ b/server/src/main/java/org/opensearch/client/Requests.java @@ -34,6 +34,7 @@ import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionStateRequest; import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionRequest; +import org.opensearch.action.admin.cluster.decommission.awareness.delete.DeleteDecommissionRequest; import org.opensearch.action.admin.cluster.health.ClusterHealthRequest; import org.opensearch.action.admin.cluster.node.info.NodesInfoRequest; import org.opensearch.action.admin.cluster.node.stats.NodesStatsRequest; @@ -568,4 +569,11 @@ public static DecommissionRequest decommissionRequest() { public static GetDecommissionStateRequest getDecommissionStateRequest() { return new GetDecommissionStateRequest(); } + + /** + * Creates a new delete decommission request. + */ + public static DeleteDecommissionRequest deleteDecommissionRequest() { + return new DeleteDecommissionRequest(); + } } diff --git a/server/src/main/java/org/opensearch/client/support/AbstractClient.java b/server/src/main/java/org/opensearch/client/support/AbstractClient.java index 253e20d84c410..71fd224e2247c 100644 --- a/server/src/main/java/org/opensearch/client/support/AbstractClient.java +++ b/server/src/main/java/org/opensearch/client/support/AbstractClient.java @@ -51,6 +51,10 @@ import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionRequest; import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionRequestBuilder; import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionResponse; +import org.opensearch.action.admin.cluster.decommission.awareness.delete.DeleteDecommissionAction; +import org.opensearch.action.admin.cluster.decommission.awareness.delete.DeleteDecommissionRequest; +import org.opensearch.action.admin.cluster.decommission.awareness.delete.DeleteDecommissionRequestBuilder; +import org.opensearch.action.admin.cluster.decommission.awareness.delete.DeleteDecommissionResponse; import org.opensearch.action.admin.cluster.health.ClusterHealthAction; import org.opensearch.action.admin.cluster.health.ClusterHealthRequest; import org.opensearch.action.admin.cluster.health.ClusterHealthRequestBuilder; @@ -1353,6 +1357,22 @@ public GetDecommissionStateRequestBuilder prepareGetDecommission() { return new GetDecommissionStateRequestBuilder(this, GetDecommissionStateAction.INSTANCE); } + @Override + public ActionFuture deleteDecommission(DeleteDecommissionRequest request) { + return execute(DeleteDecommissionAction.INSTANCE, request); + } + + @Override + public void deleteDecommission(DeleteDecommissionRequest request, ActionListener listener) { + execute(DeleteDecommissionAction.INSTANCE, request, listener); + } + + @Override + public DeleteDecommissionRequestBuilder prepareDeleteDecommission() { + return new DeleteDecommissionRequestBuilder(this, DeleteDecommissionAction.INSTANCE); + } + + } static class IndicesAdmin implements IndicesAdminClient { @@ -1895,6 +1915,7 @@ public void resolveIndex(ResolveIndexAction.Request request, ActionListener resolveIndex(ResolveIndexAction.Request request) { return execute(ResolveIndexAction.INSTANCE, request); } + } @Override diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 4caae60bf9e5c..8cb268f5533a9 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -436,4 +436,38 @@ private static void ensureNoQuorumLossDueToDecommissioning( ); } } + + public void clearDecommissionStatus(final ActionListener listener) { + clusterService.submitStateUpdateTask( + "delete_decommission", + new ClusterStateUpdateTask(Priority.URGENT) { + @Override + public ClusterState execute(ClusterState currentState) { + return deleteDecommissionAttribute(currentState); + } + + @Override + public void onFailure(String source, Exception e) { + logger.error(() -> new ParameterizedMessage("Failed to clear decommission attribute."), e); + listener.onFailure(e); + } + + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + // Once the cluster state is processed we can try to recommission nodes by setting the weights for the zone. + // TODO Set the weights for the recommissioning zone. + listener.onResponse(new ClusterStateUpdateResponse(true)); + } + } + ); + } + + ClusterState deleteDecommissionAttribute(final ClusterState currentState) { + logger.info("Delete decommission request received"); + Metadata metadata = currentState.metadata(); + Metadata.Builder mdBuilder = Metadata.builder(metadata); + mdBuilder.removeCustom(DecommissionAttributeMetadata.TYPE); + return ClusterState.builder(currentState).metadata(mdBuilder).build(); + } + } diff --git a/server/src/main/java/org/opensearch/rest/action/admin/cluster/RestDeleteDecommissionAction.java b/server/src/main/java/org/opensearch/rest/action/admin/cluster/RestDeleteDecommissionAction.java new file mode 100644 index 0000000000000..bad6ddf7b6b4c --- /dev/null +++ b/server/src/main/java/org/opensearch/rest/action/admin/cluster/RestDeleteDecommissionAction.java @@ -0,0 +1,41 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.rest.action.admin.cluster; + +import org.opensearch.action.admin.cluster.decommission.awareness.delete.DeleteDecommissionRequest; +import org.opensearch.client.Requests; +import org.opensearch.client.node.NodeClient; +import org.opensearch.rest.BaseRestHandler; +import org.opensearch.rest.RestRequest; +import org.opensearch.rest.action.RestToXContentListener; + +import java.io.IOException; +import java.util.List; + +import static java.util.Collections.singletonList; +import static org.opensearch.rest.RestRequest.Method.DELETE; + +public class RestDeleteDecommissionAction extends BaseRestHandler { + + @Override + public List routes() { + return singletonList(new Route(DELETE, "/_cluster/decommission/awareness")); + } + + @Override + public String getName() { + return "delete_decommission_action"; + } + + @Override + protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException { + DeleteDecommissionRequest deleteDecommissionRequest = Requests.deleteDecommissionRequest(); + return channel -> client.admin().cluster().deleteDecommission(deleteDecommissionRequest, new RestToXContentListener<>(channel)); + } +} From b030e925afa3b2f0d0b132f383394aaacf62c5a0 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Thu, 8 Sep 2022 12:22:57 +0530 Subject: [PATCH 171/187] Update interval --- server/src/main/java/org/opensearch/discovery/PeerFinder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/org/opensearch/discovery/PeerFinder.java b/server/src/main/java/org/opensearch/discovery/PeerFinder.java index 0cd7169ff191e..7bf8cb208a24d 100644 --- a/server/src/main/java/org/opensearch/discovery/PeerFinder.java +++ b/server/src/main/java/org/opensearch/discovery/PeerFinder.java @@ -87,7 +87,7 @@ public abstract class PeerFinder { // the time between attempts to find all peers when node is in decommissioned state, default set to 2 minutes public static final Setting DISCOVERY_FIND_PEERS_INTERVAL_DURING_DECOMMISSION_SETTING = Setting.timeSetting( "discovery.find_peers_interval_during_decommission", - TimeValue.timeValueMinutes(2L), + TimeValue.timeValueSeconds(30L), TimeValue.timeValueMillis(1000), Setting.Property.NodeScope ); From b46f25e0b2c8bb5168bd70c5713d1a8593a1dbca Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Thu, 8 Sep 2022 16:18:35 +0530 Subject: [PATCH 172/187] Fix to empty GET [cherry-pick] --- .../get/GetDecommissionStateResponse.java | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionStateResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionStateResponse.java index 538dab44c6e4c..f7b7dc5e09b0b 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionStateResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionStateResponse.java @@ -14,6 +14,7 @@ import org.opensearch.cluster.decommission.DecommissionStatus; import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.common.settings.Settings; import org.opensearch.common.xcontent.ToXContentObject; import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentParser; @@ -43,14 +44,30 @@ public class GetDecommissionStateResponse extends ActionResponse implements ToXC } GetDecommissionStateResponse(StreamInput in) throws IOException { - this.decommissionedAttribute = new DecommissionAttribute(in); - this.status = DecommissionStatus.fromString(in.readString()); + if (in.readBoolean()) { + this.decommissionedAttribute = new DecommissionAttribute(in); + } else { + this.decommissionedAttribute = null; + } + if (in.readBoolean()) { + this.status = DecommissionStatus.fromString(in.readString()); + } else { + this.status = null; + } } @Override public void writeTo(StreamOutput out) throws IOException { - decommissionedAttribute.writeTo(out); - out.writeString(status.status()); + boolean isNotNullDecommissionAttribute = this.decommissionedAttribute != null; + boolean isNotNullStatus = this.status != null; + out.writeBoolean(isNotNullDecommissionAttribute); + if (isNotNullDecommissionAttribute) { + decommissionedAttribute.writeTo(out); + } + out.writeBoolean(isNotNullStatus); + if (isNotNullStatus) { + out.writeString(status.status()); + } } public DecommissionAttribute getDecommissionedAttribute() { From b65e67f77b99f11f4f028b40ffd9d0615027496c Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Thu, 8 Sep 2022 18:58:50 +0530 Subject: [PATCH 173/187] Resolve comments Signed-off-by: Rishab Nahata --- .../decommission/DecommissionController.java | 5 +- .../decommission/DecommissionService.java | 226 ++++++++---------- 2 files changed, 109 insertions(+), 122 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java index cff01e4e480a5..2f860d5318216 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java @@ -29,6 +29,7 @@ import org.opensearch.cluster.routing.allocation.AllocationService; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Priority; +import org.opensearch.common.Strings; import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.unit.TimeValue; import org.opensearch.threadpool.ThreadPool; @@ -74,14 +75,14 @@ public class DecommissionController { /** * Transport call to add nodes to voting config exclusion * - * @param nodes set of nodes to be added to voting config exclusion list + * @param nodes set of nodes Ids to be added to voting config exclusion list * @param listener callback for response or failure */ public void excludeDecommissionedNodesFromVotingConfig(Set nodes, ActionListener listener) { transportService.sendRequest( transportService.getLocalNode(), AddVotingConfigExclusionsAction.NAME, - new AddVotingConfigExclusionsRequest(nodes.stream().toArray(String[]::new)), + new AddVotingConfigExclusionsRequest(Strings.EMPTY_ARRAY, nodes.toArray(String[]::new), Strings.EMPTY_ARRAY, TimeValue.timeValueSeconds(30)), new TransportResponseHandler() { @Override public void handleResponse(AddVotingConfigExclusionsResponse response) { diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 50c7889400f95..65e38f0fd0326 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -28,7 +28,6 @@ import org.opensearch.common.unit.TimeValue; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; -import org.opensearch.cluster.coordination.CoordinationMetadata.VotingConfigExclusion; import java.util.HashMap; import java.util.HashSet; @@ -36,6 +35,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Predicate; import java.util.stream.Collectors; import static org.opensearch.cluster.routing.allocation.decider.AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING; @@ -130,14 +130,7 @@ public ClusterState execute(ClusterState currentState) throws Exception { Metadata.Builder mdBuilder = Metadata.builder(metadata); DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.custom(DecommissionAttributeMetadata.TYPE); // check the request sanity and reject the request if there's any inflight or successful request already present - ensureNoInflightDifferentDecommissionRequest(decommissionAttributeMetadata, decommissionAttribute); - // check if the same attribute is requested for decommission and currently not FAILED, then return the current state as is - if (decommissionAttributeMetadata != null - && decommissionAttributeMetadata.decommissionAttribute().equals(decommissionAttribute) - && !decommissionAttributeMetadata.status().equals(DecommissionStatus.FAILED)) { - logger.info("re-request received for decommissioning [{}], will not update state", decommissionAttribute); - return currentState; - } + ensureNoInflightRequest(decommissionAttributeMetadata, decommissionAttribute); decommissionAttributeMetadata = new DecommissionAttributeMetadata(decommissionAttribute); mdBuilder.putCustom(DecommissionAttributeMetadata.TYPE, decommissionAttributeMetadata); logger.info("registering decommission metadata [{}] to execute action", decommissionAttributeMetadata.toString()); @@ -172,102 +165,90 @@ private void decommissionClusterManagerNodes( ) { ClusterState state = clusterService.getClusterApplierService().state(); Set clusterManagerNodesToBeDecommissioned = filterNodesWithDecommissionAttribute(state, decommissionAttribute, true); - try { - // this is a sanity check that the cluster will not go into a quorum loss state because of exclusion - ensureNoQuorumLossDueToDecommissioning( - decommissionAttribute, - clusterManagerNodesToBeDecommissioned, - state.getLastCommittedConfiguration() - ); - } catch (DecommissioningFailedException dfe) { - listener.onFailure(dfe); - decommissionController.updateMetadataWithDecommissionStatus( - DecommissionStatus.FAILED, - new ActionListener() { + // This check doesn't seem to be needed as exclusion automatically shrinks the config before sending the response. + // We can guarantee that because of exclusion there wouldn't be a quorum loss and if the service gets a successful response, + // we are certain that the config is updated and nodes are ready to be kicked out. + // Please add comment if you feel there could be a edge case here. +// try { +// // this is a sanity check that the cluster will not go into a quorum loss state because of exclusion +// ensureNoQuorumLossDueToDecommissioning( +// decommissionAttribute, +// clusterManagerNodesToBeDecommissioned, +// state.getLastCommittedConfiguration() +// ); +// } catch (DecommissioningFailedException dfe) { +// listener.onFailure(dfe); +// decommissionController.updateMetadataWithDecommissionStatus(DecommissionStatus.FAILED, statusUpdateListener()); +// return; +// } + + ActionListener exclusionListener = new ActionListener() { + @Override + public void onResponse(Void unused) { + if (transportService.getLocalNode().isClusterManagerNode() + && !nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute) + ) { + // we are good here to send the response now as the request is processed by an eligible active leader + // and to-be-decommissioned cluster manager is no more part of Voting Configuration + listener.onResponse(new ClusterStateUpdateResponse(true)); + failDecommissionedNodes(clusterService.getClusterApplierService().state()); + } else { + // explicitly calling listener.onFailure with NotClusterManagerException as we can certainly say that + // the local cluster manager node will be abdicated and soon will no longer be cluster manager. + // this will ensure that request is retried until cluster manager times out + listener.onFailure( + new NotClusterManagerException( + "node [" + + transportService.getLocalNode().toString() + + "] not eligible to execute decommission request. Will retry until timeout." + ) + ); + } + } + + @Override + public void onFailure(Exception e) { + listener.onFailure(e); + decommissionController.updateMetadataWithDecommissionStatus(DecommissionStatus.FAILED, statusUpdateListener()); + } + }; + + // remove all 'to-be-decommissioned' cluster manager eligible nodes from voting config + Set nodeIdsToBeExcluded = clusterManagerNodesToBeDecommissioned.stream() + .map(DiscoveryNode::getId) + .collect(Collectors.toSet()); + + final Predicate allNodesRemoved = clusterState -> { + final Set votingConfigNodeIds = clusterState.getLastCommittedConfiguration().getNodeIds(); + return nodeIdsToBeExcluded.stream().noneMatch(votingConfigNodeIds::contains); + }; + if (allNodesRemoved.test(clusterService.getClusterApplierService().state())) { + exclusionListener.onResponse(null); + } else { + // send a transport request to exclude to-be-decommissioned cluster manager eligible nodes from voting config + decommissionController.excludeDecommissionedNodesFromVotingConfig( + nodeIdsToBeExcluded, + new ActionListener() { @Override - public void onResponse(DecommissionStatus status) { + public void onResponse(Void unused) { logger.info( - "updated the status to [{}], as cluster could have gone to quorum loss situation due to decommissioning", - status.toString() + "successfully removed decommissioned cluster manager eligible nodes [{}] from voting config ", + clusterManagerNodesToBeDecommissioned.toString() ); + exclusionListener.onResponse(null); } @Override public void onFailure(Exception e) { - logger.error("unexpected error found while updating the status", e); + logger.debug( + new ParameterizedMessage("failure in removing decommissioned cluster manager eligible nodes from voting config"), + e + ); + exclusionListener.onFailure(e); } } ); } - // remove all 'to-be-decommissioned' cluster manager eligible nodes from voting config - // The method ensures that we don't exclude same nodes multiple times - boolean toBeDecommissionedClusterManagerNodesExcluded = excludeDecommissionedClusterManagerNodesFromVotingConfig( - clusterManagerNodesToBeDecommissioned - ); - - if (transportService.getLocalNode().isClusterManagerNode() - && !nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute) - && toBeDecommissionedClusterManagerNodesExcluded) { - // we are good here to send the response now as the request is processed by an eligible active leader - // and to-be-decommissioned cluster manager is no more part of Voting Configuration - listener.onResponse(new ClusterStateUpdateResponse(true)); - failDecommissionedNodes(clusterService.getClusterApplierService().state()); - } else { - // explicitly calling listener.onFailure with NotClusterManagerException as we can certainly say the local cluster manager node - // will - // be abdicated and soon will no longer be cluster manager. - // this will ensure that request is retried until cluster manager times out - listener.onFailure( - new NotClusterManagerException( - "node [" - + transportService.getLocalNode().toString() - + "] not eligible to execute decommission request. Will retry until timeout." - ) - ); - } - } - - private boolean excludeDecommissionedClusterManagerNodesFromVotingConfig(Set clusterManagerNodesToBeDecommissioned) { - Set clusterManagerNodesNameToBeDecommissioned = clusterManagerNodesToBeDecommissioned.stream() - .map(DiscoveryNode::getName) - .collect(Collectors.toSet()); - - Set currentVotingConfigExclusions = clusterService.getClusterApplierService() - .state() - .coordinationMetadata() - .getVotingConfigExclusions(); - Set excludedNodesName = currentVotingConfigExclusions.stream() - .map(VotingConfigExclusion::getNodeName) - .collect(Collectors.toSet()); - - // check if the to-be-excluded nodes are excluded. If yes, we don't need to exclude them again - if (clusterManagerNodesNameToBeDecommissioned.size() == 0 - || excludedNodesName.containsAll(clusterManagerNodesNameToBeDecommissioned)) { - return true; - } - // send a transport request to exclude to-be-decommissioned cluster manager eligible nodes from voting config - decommissionController.excludeDecommissionedNodesFromVotingConfig( - clusterManagerNodesNameToBeDecommissioned, - new ActionListener() { - @Override - public void onResponse(Void unused) { - logger.info( - "successfully removed decommissioned cluster manager eligible nodes [{}] from voting config ", - clusterManagerNodesToBeDecommissioned.toString() - ); - } - - @Override - public void onFailure(Exception e) { - logger.debug( - new ParameterizedMessage("failure in removing decommissioned cluster manager eligible nodes from voting config"), - e - ); - } - } - ); - // send false for now and let the transport request be retried - return false; } private void failDecommissionedNodes(ClusterState state) { @@ -313,17 +294,6 @@ public void onFailure(Exception e) { } private void clearVotingConfigExclusionAndUpdateStatus(boolean decommissionSuccessful) { - ActionListener statusUpdateListener = new ActionListener<>() { - @Override - public void onResponse(DecommissionStatus status) { - logger.info("completed decommission action"); - } - - @Override - public void onFailure(Exception e) { - logger.error("failure encountered while executing decommission action"); - } - }; decommissionController.clearVotingConfigExclusion(new ActionListener() { @Override public void onResponse(Void unused) { @@ -331,7 +301,7 @@ public void onResponse(Void unused) { "successfully cleared voting config exclusion after completing decommission action, proceeding to update metadata" ); DecommissionStatus updateStatusWith = decommissionSuccessful ? DecommissionStatus.SUCCESSFUL : DecommissionStatus.FAILED; - decommissionController.updateMetadataWithDecommissionStatus(updateStatusWith, statusUpdateListener); + decommissionController.updateMetadataWithDecommissionStatus(updateStatusWith, statusUpdateListener()); } @Override @@ -340,7 +310,7 @@ public void onFailure(Exception e) { new ParameterizedMessage("failure in clearing voting config exclusion after processing decommission request"), e ); - decommissionController.updateMetadataWithDecommissionStatus(DecommissionStatus.FAILED, statusUpdateListener); + decommissionController.updateMetadataWithDecommissionStatus(DecommissionStatus.FAILED, statusUpdateListener()); } }); } @@ -391,24 +361,24 @@ private static void validateAwarenessAttribute( } } - private static void ensureNoInflightDifferentDecommissionRequest( + private static void ensureNoInflightRequest( DecommissionAttributeMetadata decommissionAttributeMetadata, DecommissionAttribute decommissionAttribute ) { String msg = null; if (decommissionAttributeMetadata != null) { - if (decommissionAttributeMetadata.status().equals(DecommissionStatus.SUCCESSFUL)) { - // one awareness attribute is already decommissioned. We will reject the new request - msg = "one awareness attribute already successfully decommissioned. Recommission before triggering another decommission"; - } else if (decommissionAttributeMetadata.status().equals(DecommissionStatus.FAILED)) { - // here we are sure that the previous decommission request failed, we can let this request pass this check - return; - } else { - // it means the decommission has been initiated or is inflight. In that case, if the same attribute is requested for - // decommissioning, which can happen during retries, we will pass this check, if not, we will throw exception - if (!decommissionAttributeMetadata.decommissionAttribute().equals(decommissionAttribute)) { - msg = "another request for decommission is in flight, will not process this request"; - } + switch (decommissionAttributeMetadata.status()) { + case SUCCESSFUL: + // one awareness attribute is already decommissioned. We will reject the new request + msg = "one awareness attribute already successfully decommissioned, recommission before triggering another decommission"; + break; + case IN_PROGRESS: + case INIT: + // it means the decommission has been initiated or is inflight. In that case, will fail new request + msg = "there's an inflight decommission request in progress, cannot process this request"; + break; + case FAILED: + break; } } if (msg != null) { @@ -435,4 +405,20 @@ private static void ensureNoQuorumLossDueToDecommissioning( ); } } + + private ActionListener statusUpdateListener() { + return new ActionListener() { + @Override + public void onResponse(DecommissionStatus status) { + logger.info( + "updated the status to [{}]", status.toString() + ); + } + + @Override + public void onFailure(Exception e) { + logger.error("unexpected failure during status update", e); + } + }; + } } From 5e5b4351381618176dece1836857e5a85f4e2f26 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Thu, 8 Sep 2022 20:08:24 +0530 Subject: [PATCH 174/187] Precheck for retry Signed-off-by: Rishab Nahata --- .../cluster/decommission/DecommissionService.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index e9e4509bee090..911246e33798d 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -130,6 +130,15 @@ public ClusterState execute(ClusterState currentState) throws Exception { Metadata metadata = currentState.metadata(); Metadata.Builder mdBuilder = Metadata.builder(metadata); DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.custom(DecommissionAttributeMetadata.TYPE); + // check if the same attribute is requested for decommission and currently not FAILED or SUCCESS, then return the current state as is + if (decommissionAttributeMetadata != null + && decommissionAttributeMetadata.decommissionAttribute().equals(decommissionAttribute) + && !decommissionAttributeMetadata.status().equals(DecommissionStatus.FAILED) + && !decommissionAttributeMetadata.status().equals(DecommissionStatus.SUCCESSFUL) + ) { + logger.info("re-request received for decommissioning [{}], will not update state", decommissionAttribute); + return currentState; + } // check the request sanity and reject the request if there's any inflight or successful request already present ensureNoInflightRequest(decommissionAttributeMetadata, decommissionAttribute); decommissionAttributeMetadata = new DecommissionAttributeMetadata(decommissionAttribute); @@ -160,7 +169,7 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS }); } - private void decommissionClusterManagerNodes( + private synchronized void decommissionClusterManagerNodes( final DecommissionAttribute decommissionAttribute, ActionListener listener ) { From 74041c7dfc30d69ea6871290f62bc3a23d76bcb0 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Thu, 8 Sep 2022 20:08:24 +0530 Subject: [PATCH 175/187] Precheck for retry Signed-off-by: Rishab Nahata --- .../cluster/decommission/DecommissionService.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 65e38f0fd0326..0f67187bcc7f3 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -129,6 +129,15 @@ public ClusterState execute(ClusterState currentState) throws Exception { Metadata metadata = currentState.metadata(); Metadata.Builder mdBuilder = Metadata.builder(metadata); DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.custom(DecommissionAttributeMetadata.TYPE); + // check if the same attribute is requested for decommission and currently not FAILED or SUCCESS, then return the current state as is + if (decommissionAttributeMetadata != null + && decommissionAttributeMetadata.decommissionAttribute().equals(decommissionAttribute) + && !decommissionAttributeMetadata.status().equals(DecommissionStatus.FAILED) + && !decommissionAttributeMetadata.status().equals(DecommissionStatus.SUCCESSFUL) + ) { + logger.info("re-request received for decommissioning [{}], will not update state", decommissionAttribute); + return currentState; + } // check the request sanity and reject the request if there's any inflight or successful request already present ensureNoInflightRequest(decommissionAttributeMetadata, decommissionAttribute); decommissionAttributeMetadata = new DecommissionAttributeMetadata(decommissionAttribute); @@ -159,7 +168,7 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS }); } - private void decommissionClusterManagerNodes( + private synchronized void decommissionClusterManagerNodes( final DecommissionAttribute decommissionAttribute, ActionListener listener ) { From 88a1734430003ab3c07e913775848ff5814dbf69 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Thu, 8 Sep 2022 20:30:51 +0530 Subject: [PATCH 176/187] Add logging Signed-off-by: Rishab Nahata --- .../cluster/decommission/DecommissionService.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 0f67187bcc7f3..6cefbdca17fc3 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -197,6 +197,7 @@ public void onResponse(Void unused) { if (transportService.getLocalNode().isClusterManagerNode() && !nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute) ) { + logger.info("will attempt to fail decommissioned nodes as local node is eligible to process the request"); // we are good here to send the response now as the request is processed by an eligible active leader // and to-be-decommissioned cluster manager is no more part of Voting Configuration listener.onResponse(new ClusterStateUpdateResponse(true)); @@ -205,6 +206,8 @@ public void onResponse(Void unused) { // explicitly calling listener.onFailure with NotClusterManagerException as we can certainly say that // the local cluster manager node will be abdicated and soon will no longer be cluster manager. // this will ensure that request is retried until cluster manager times out + logger.info("local node is not eligible to process the request, " + + "throwing NotClusterManagerException to attempt a retry on an eligible node"); listener.onFailure( new NotClusterManagerException( "node [" @@ -218,6 +221,7 @@ public void onResponse(Void unused) { @Override public void onFailure(Exception e) { listener.onFailure(e); + // attempting to mark the status as FAILED decommissionController.updateMetadataWithDecommissionStatus(DecommissionStatus.FAILED, statusUpdateListener()); } }; @@ -379,12 +383,14 @@ private static void ensureNoInflightRequest( switch (decommissionAttributeMetadata.status()) { case SUCCESSFUL: // one awareness attribute is already decommissioned. We will reject the new request - msg = "one awareness attribute already successfully decommissioned, recommission before triggering another decommission"; + msg = "one awareness attribute [" + decommissionAttributeMetadata.decommissionAttribute().toString() + + "] already successfully decommissioned, recommission before triggering another decommission"; break; case IN_PROGRESS: case INIT: // it means the decommission has been initiated or is inflight. In that case, will fail new request - msg = "there's an inflight decommission request in progress, cannot process this request"; + msg = "there's an inflight decommission request for attribute [" + decommissionAttributeMetadata.decommissionAttribute().toString() + + "] is in progress, cannot process this request"; break; case FAILED: break; From 96f04a4966185e6e57abcd2df53dc068a3395d01 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Thu, 8 Sep 2022 20:40:04 +0530 Subject: [PATCH 177/187] Fix spotless check Signed-off-by: Rishab Nahata --- .../decommission/DecommissionController.java | 7 +- .../decommission/DecommissionService.java | 90 +++++++++---------- 2 files changed, 50 insertions(+), 47 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java index 2f860d5318216..244339b7ccd03 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java @@ -82,7 +82,12 @@ public void excludeDecommissionedNodesFromVotingConfig(Set nodes, Action transportService.sendRequest( transportService.getLocalNode(), AddVotingConfigExclusionsAction.NAME, - new AddVotingConfigExclusionsRequest(Strings.EMPTY_ARRAY, nodes.toArray(String[]::new), Strings.EMPTY_ARRAY, TimeValue.timeValueSeconds(30)), + new AddVotingConfigExclusionsRequest( + Strings.EMPTY_ARRAY, + nodes.toArray(String[]::new), + Strings.EMPTY_ARRAY, + TimeValue.timeValueSeconds(30) + ), new TransportResponseHandler() { @Override public void handleResponse(AddVotingConfigExclusionsResponse response) { diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 6cefbdca17fc3..9e1c10c5167d6 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -129,12 +129,12 @@ public ClusterState execute(ClusterState currentState) throws Exception { Metadata metadata = currentState.metadata(); Metadata.Builder mdBuilder = Metadata.builder(metadata); DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.custom(DecommissionAttributeMetadata.TYPE); - // check if the same attribute is requested for decommission and currently not FAILED or SUCCESS, then return the current state as is + // check if the same attribute is requested for decommission and currently not FAILED or SUCCESS, then return the current + // state as is if (decommissionAttributeMetadata != null && decommissionAttributeMetadata.decommissionAttribute().equals(decommissionAttribute) && !decommissionAttributeMetadata.status().equals(DecommissionStatus.FAILED) - && !decommissionAttributeMetadata.status().equals(DecommissionStatus.SUCCESSFUL) - ) { + && !decommissionAttributeMetadata.status().equals(DecommissionStatus.SUCCESSFUL)) { logger.info("re-request received for decommissioning [{}], will not update state", decommissionAttribute); return currentState; } @@ -178,25 +178,24 @@ private synchronized void decommissionClusterManagerNodes( // We can guarantee that because of exclusion there wouldn't be a quorum loss and if the service gets a successful response, // we are certain that the config is updated and nodes are ready to be kicked out. // Please add comment if you feel there could be a edge case here. -// try { -// // this is a sanity check that the cluster will not go into a quorum loss state because of exclusion -// ensureNoQuorumLossDueToDecommissioning( -// decommissionAttribute, -// clusterManagerNodesToBeDecommissioned, -// state.getLastCommittedConfiguration() -// ); -// } catch (DecommissioningFailedException dfe) { -// listener.onFailure(dfe); -// decommissionController.updateMetadataWithDecommissionStatus(DecommissionStatus.FAILED, statusUpdateListener()); -// return; -// } + // try { + // // this is a sanity check that the cluster will not go into a quorum loss state because of exclusion + // ensureNoQuorumLossDueToDecommissioning( + // decommissionAttribute, + // clusterManagerNodesToBeDecommissioned, + // state.getLastCommittedConfiguration() + // ); + // } catch (DecommissioningFailedException dfe) { + // listener.onFailure(dfe); + // decommissionController.updateMetadataWithDecommissionStatus(DecommissionStatus.FAILED, statusUpdateListener()); + // return; + // } ActionListener exclusionListener = new ActionListener() { @Override public void onResponse(Void unused) { if (transportService.getLocalNode().isClusterManagerNode() - && !nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute) - ) { + && !nodeHasDecommissionedAttribute(transportService.getLocalNode(), decommissionAttribute)) { logger.info("will attempt to fail decommissioned nodes as local node is eligible to process the request"); // we are good here to send the response now as the request is processed by an eligible active leader // and to-be-decommissioned cluster manager is no more part of Voting Configuration @@ -206,8 +205,10 @@ public void onResponse(Void unused) { // explicitly calling listener.onFailure with NotClusterManagerException as we can certainly say that // the local cluster manager node will be abdicated and soon will no longer be cluster manager. // this will ensure that request is retried until cluster manager times out - logger.info("local node is not eligible to process the request, " + - "throwing NotClusterManagerException to attempt a retry on an eligible node"); + logger.info( + "local node is not eligible to process the request, " + + "throwing NotClusterManagerException to attempt a retry on an eligible node" + ); listener.onFailure( new NotClusterManagerException( "node [" @@ -239,28 +240,25 @@ public void onFailure(Exception e) { exclusionListener.onResponse(null); } else { // send a transport request to exclude to-be-decommissioned cluster manager eligible nodes from voting config - decommissionController.excludeDecommissionedNodesFromVotingConfig( - nodeIdsToBeExcluded, - new ActionListener() { - @Override - public void onResponse(Void unused) { - logger.info( - "successfully removed decommissioned cluster manager eligible nodes [{}] from voting config ", - clusterManagerNodesToBeDecommissioned.toString() - ); - exclusionListener.onResponse(null); - } + decommissionController.excludeDecommissionedNodesFromVotingConfig(nodeIdsToBeExcluded, new ActionListener() { + @Override + public void onResponse(Void unused) { + logger.info( + "successfully removed decommissioned cluster manager eligible nodes [{}] from voting config ", + clusterManagerNodesToBeDecommissioned.toString() + ); + exclusionListener.onResponse(null); + } - @Override - public void onFailure(Exception e) { - logger.debug( - new ParameterizedMessage("failure in removing decommissioned cluster manager eligible nodes from voting config"), - e - ); - exclusionListener.onFailure(e); - } + @Override + public void onFailure(Exception e) { + logger.debug( + new ParameterizedMessage("failure in removing decommissioned cluster manager eligible nodes from voting config"), + e + ); + exclusionListener.onFailure(e); } - ); + }); } } @@ -383,14 +381,16 @@ private static void ensureNoInflightRequest( switch (decommissionAttributeMetadata.status()) { case SUCCESSFUL: // one awareness attribute is already decommissioned. We will reject the new request - msg = "one awareness attribute [" + decommissionAttributeMetadata.decommissionAttribute().toString() + - "] already successfully decommissioned, recommission before triggering another decommission"; + msg = "one awareness attribute [" + + decommissionAttributeMetadata.decommissionAttribute().toString() + + "] already successfully decommissioned, recommission before triggering another decommission"; break; case IN_PROGRESS: case INIT: // it means the decommission has been initiated or is inflight. In that case, will fail new request - msg = "there's an inflight decommission request for attribute [" + decommissionAttributeMetadata.decommissionAttribute().toString() + - "] is in progress, cannot process this request"; + msg = "there's an inflight decommission request for attribute [" + + decommissionAttributeMetadata.decommissionAttribute().toString() + + "] is in progress, cannot process this request"; break; case FAILED: break; @@ -425,9 +425,7 @@ private ActionListener statusUpdateListener() { return new ActionListener() { @Override public void onResponse(DecommissionStatus status) { - logger.info( - "updated the status to [{}]", status.toString() - ); + logger.info("updated the status to [{}]", status.toString()); } @Override From e305e1e45fae143e5dcf023bd55f4992814a2bfa Mon Sep 17 00:00:00 2001 From: Suraj Singh Date: Thu, 8 Sep 2022 09:46:23 -0700 Subject: [PATCH 178/187] Add bwcVersion 2.4.0 (#4455) * Add bwcVersion 2.4.0 Signed-off-by: Suraj Singh * Add changelog entry Signed-off-by: Suraj Singh Signed-off-by: Suraj Singh --- .ci/bwcVersions | 1 + CHANGELOG.md | 4 +++- server/src/main/java/org/opensearch/Version.java | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.ci/bwcVersions b/.ci/bwcVersions index 914426eebe35e..1dc8dc955f7c6 100644 --- a/.ci/bwcVersions +++ b/.ci/bwcVersions @@ -51,3 +51,4 @@ BWC_VERSION: - "2.2.1" - "2.2.2" - "2.3.0" + - "2.4.0" diff --git a/CHANGELOG.md b/CHANGELOG.md index ec8441831448f..303934c7995b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Support for HTTP/2 (server-side) ([#3847](https://github.com/opensearch-project/OpenSearch/pull/3847)) - BWC version 2.2.2 ([#4383](https://github.com/opensearch-project/OpenSearch/pull/4383)) - Support for labels on version bump PRs, skip label support for changelog verifier ([#4391](https://github.com/opensearch-project/OpenSearch/pull/4391)) +- Update previous release bwc version to 2.4.0 ([#4455](https://github.com/opensearch-project/OpenSearch/pull/4455)) + ### Dependencies - Bumps `org.gradle.test-retry` from 1.4.0 to 1.4.1 - Bumps `reactor-netty-core` from 1.0.19 to 1.0.22 @@ -78,4 +80,4 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) [Unreleased]: https://github.com/opensearch-project/OpenSearch/compare/2.2.0...HEAD -[2.x]: https://github.com/opensearch-project/OpenSearch/compare/2.2.0...2.x \ No newline at end of file +[2.x]: https://github.com/opensearch-project/OpenSearch/compare/2.2.0...2.x diff --git a/server/src/main/java/org/opensearch/Version.java b/server/src/main/java/org/opensearch/Version.java index 10e5f16419a7a..978f0ee2186f2 100644 --- a/server/src/main/java/org/opensearch/Version.java +++ b/server/src/main/java/org/opensearch/Version.java @@ -98,6 +98,7 @@ public class Version implements Comparable, ToXContentFragment { public static final Version V_2_2_1 = new Version(2020199, org.apache.lucene.util.Version.LUCENE_9_3_0); public static final Version V_2_2_2 = new Version(2020299, org.apache.lucene.util.Version.LUCENE_9_3_0); public static final Version V_2_3_0 = new Version(2030099, org.apache.lucene.util.Version.LUCENE_9_3_0); + public static final Version V_2_4_0 = new Version(2040099, org.apache.lucene.util.Version.LUCENE_9_3_0); public static final Version V_3_0_0 = new Version(3000099, org.apache.lucene.util.Version.LUCENE_9_4_0); public static final Version CURRENT = V_3_0_0; From 517c19c9725e53663a280f9025679c2f9e659948 Mon Sep 17 00:00:00 2001 From: Andrija Pantovic Date: Thu, 8 Sep 2022 23:53:36 +0200 Subject: [PATCH 179/187] add support for s390x architecture (#4001) * add s390x support in systemcallfilter https://github.com/opensearch-project/OpenSearch/issues/4000 Signed-off-by: Andrija Pantovic * add gradle cfg for s390x Signed-off-by: Andrija Pantovic * change assertion for architecture Signed-off-by: Andrija Pantovic * change assertion for architecture Signed-off-by: Andrija Pantovic * update changelog Signed-off-by: Andrija Pantovic Signed-off-by: Andrija Pantovic Signed-off-by: Andrija Pantovic --- CHANGELOG.md | 1 + .../org/opensearch/gradle/Architecture.java | 5 ++- .../gradle/DistributionDownloadPlugin.java | 3 ++ .../main/java/org/opensearch/gradle/Jdk.java | 2 +- .../opensearch/gradle/ArchitectureTests.java | 45 +++++++++++++++++++ .../gradle/JdkDownloadPluginTests.java | 2 +- distribution/archives/build.gradle | 7 +++ distribution/build.gradle | 4 +- distribution/docker/build.gradle | 8 ++++ .../docker/docker-s390x-export/build.gradle | 13 ++++++ .../bootstrap/SystemCallFilter.java | 1 + settings.gradle | 2 + 12 files changed, 88 insertions(+), 5 deletions(-) create mode 100644 buildSrc/src/test/java/org/opensearch/gradle/ArchitectureTests.java create mode 100644 distribution/docker/docker-s390x-export/build.gradle diff --git a/CHANGELOG.md b/CHANGELOG.md index 303934c7995b2..75fde94ee7878 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ## [Unreleased] ### Added +- Add support for s390x architecture ([#4001](https://github.com/opensearch-project/OpenSearch/pull/4001)) - Github workflow for changelog verification ([#4085](https://github.com/opensearch-project/OpenSearch/pull/4085)) - Point in time rest layer changes for create and delete PIT API ([#4064](https://github.com/opensearch-project/OpenSearch/pull/4064)) - Added @dreamer-89 as an Opensearch maintainer ([#4342](https://github.com/opensearch-project/OpenSearch/pull/4342)) diff --git a/buildSrc/src/main/java/org/opensearch/gradle/Architecture.java b/buildSrc/src/main/java/org/opensearch/gradle/Architecture.java index 38d6db8c9916e..2bd87d6fa50b2 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/Architecture.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/Architecture.java @@ -35,7 +35,8 @@ public enum Architecture { X64, - ARM64; + ARM64, + S390X; public static Architecture current() { final String architecture = System.getProperty("os.arch", ""); @@ -45,6 +46,8 @@ public static Architecture current() { return X64; case "aarch64": return ARM64; + case "s390x": + return S390X; default: throw new IllegalArgumentException("can not determine architecture from [" + architecture + "]"); } diff --git a/buildSrc/src/main/java/org/opensearch/gradle/DistributionDownloadPlugin.java b/buildSrc/src/main/java/org/opensearch/gradle/DistributionDownloadPlugin.java index fccdc49ef6fc9..ae7b0d938e8ef 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/DistributionDownloadPlugin.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/DistributionDownloadPlugin.java @@ -247,6 +247,9 @@ private String dependencyNotation(OpenSearchDistribution distribution) { case X64: classifier = ":" + distribution.getPlatform() + "-x64"; break; + case S390X: + classifier = ":" + distribution.getPlatform() + "-s390x"; + break; default: throw new IllegalArgumentException("Unsupported architecture: " + distribution.getArchitecture()); } diff --git a/buildSrc/src/main/java/org/opensearch/gradle/Jdk.java b/buildSrc/src/main/java/org/opensearch/gradle/Jdk.java index 53fd998bcc53f..4b289de3f0619 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/Jdk.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/Jdk.java @@ -48,7 +48,7 @@ public class Jdk implements Buildable, Iterable { - private static final List ALLOWED_ARCHITECTURES = Collections.unmodifiableList(Arrays.asList("aarch64", "x64")); + private static final List ALLOWED_ARCHITECTURES = Collections.unmodifiableList(Arrays.asList("aarch64", "x64", "s390x")); private static final List ALLOWED_VENDORS = Collections.unmodifiableList(Arrays.asList("adoptium", "adoptopenjdk", "openjdk")); private static final List ALLOWED_PLATFORMS = Collections.unmodifiableList( Arrays.asList("darwin", "freebsd", "linux", "mac", "windows") diff --git a/buildSrc/src/test/java/org/opensearch/gradle/ArchitectureTests.java b/buildSrc/src/test/java/org/opensearch/gradle/ArchitectureTests.java new file mode 100644 index 0000000000000..05f920c6c9248 --- /dev/null +++ b/buildSrc/src/test/java/org/opensearch/gradle/ArchitectureTests.java @@ -0,0 +1,45 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.gradle; + +import org.opensearch.gradle.test.GradleUnitTestCase; + +public class ArchitectureTests extends GradleUnitTestCase { + + final String architecture = System.getProperty("os.arch", ""); + + public void testCurrentArchitecture() { + assertEquals(Architecture.X64, currentArchitecture("amd64")); + assertEquals(Architecture.X64, currentArchitecture("x86_64")); + assertEquals(Architecture.ARM64, currentArchitecture("aarch64")); + assertEquals(Architecture.S390X, currentArchitecture("s390x")); + } + + public void testInvalidCurrentArchitecture() { + assertThrows("can not determine architecture from [", IllegalArgumentException.class, () -> currentArchitecture("fooBar64")); + } + + /** + * Determines the return value of {@link Architecture#current()} based on a string representing a potential OS Architecture. + * + * @param osArchToTest An expected value of the {@code os.arch} system property on another architecture. + * @return the value of the {@link Architecture} enum which would have resulted with the given value. + * @throws IllegalArgumentException if the string is not mapped to a value of the {@link Architecture} enum. + */ + private Architecture currentArchitecture(String osArchToTest) throws IllegalArgumentException { + // Test new architecture + System.setProperty("os.arch", osArchToTest); + try { + return Architecture.current(); + } finally { + // Restore actual architecture property value + System.setProperty("os.arch", this.architecture); + } + } +} diff --git a/buildSrc/src/test/java/org/opensearch/gradle/JdkDownloadPluginTests.java b/buildSrc/src/test/java/org/opensearch/gradle/JdkDownloadPluginTests.java index 4dcc65cca4c62..ad17032e718d2 100644 --- a/buildSrc/src/test/java/org/opensearch/gradle/JdkDownloadPluginTests.java +++ b/buildSrc/src/test/java/org/opensearch/gradle/JdkDownloadPluginTests.java @@ -108,7 +108,7 @@ public void testUnknownArchitecture() { "11.0.2+33", "linux", "unknown", - "unknown architecture [unknown] for jdk [testjdk], must be one of [aarch64, x64]" + "unknown architecture [unknown] for jdk [testjdk], must be one of [aarch64, x64, s390x]" ); } diff --git a/distribution/archives/build.gradle b/distribution/archives/build.gradle index ac70ee04444c7..1376b8d419f6e 100644 --- a/distribution/archives/build.gradle +++ b/distribution/archives/build.gradle @@ -151,6 +151,13 @@ distribution_archives { } } + linuxS390xTar { + archiveClassifier = 'linux-s390x' + content { + archiveFiles(modulesFiles('linux-s390x'), 'tar', 'linux', 's390x', false) + } + } + windowsZip { archiveClassifier = 'windows-x64' content { diff --git a/distribution/build.gradle b/distribution/build.gradle index 21b7d85a7ef2b..ee9016210efc7 100644 --- a/distribution/build.gradle +++ b/distribution/build.gradle @@ -280,7 +280,7 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) { // Setup all required JDKs project.jdks { ['darwin', 'linux', 'windows'].each { platform -> - (platform == 'linux' || platform == 'darwin' ? ['x64', 'aarch64'] : ['x64']).each { architecture -> + (platform == 'linux' || platform == 'darwin' ? ['x64', 'aarch64', 's390x'] : ['x64']).each { architecture -> "bundled_${platform}_${architecture}" { it.platform = platform it.version = VersionProperties.getBundledJdk(platform) @@ -353,7 +353,7 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) { } } def buildModules = buildModulesTaskProvider - List excludePlatforms = ['darwin-x64', 'freebsd-x64', 'linux-x64', 'linux-arm64', 'windows-x64', 'darwin-arm64'] + List excludePlatforms = ['darwin-x64', 'freebsd-x64', 'linux-x64', 'linux-arm64', 'linux-s390x', 'windows-x64', 'darwin-arm64'] if (platform != null) { excludePlatforms.remove(excludePlatforms.indexOf(platform)) } else { diff --git a/distribution/docker/build.gradle b/distribution/docker/build.gradle index f5d8048a06276..7e0007f04c940 100644 --- a/distribution/docker/build.gradle +++ b/distribution/docker/build.gradle @@ -27,11 +27,13 @@ testFixtures.useFixture() configurations { arm64DockerSource + s390xDockerSource dockerSource } dependencies { arm64DockerSource project(path: ":distribution:archives:linux-arm64-tar", configuration:"default") + s390xDockerSource project(path: ":distribution:archives:linux-s390x-tar", configuration:"default") dockerSource project(path: ":distribution:archives:linux-tar", configuration:"default") } @@ -42,6 +44,8 @@ ext.expansions = { Architecture architecture, DockerBase base, boolean local -> classifier = "linux-arm64" } else if (architecture == Architecture.X64) { classifier = "linux-x64" + } else if (architecture == Architecture.S390X) { + classifier = "linux-s390x" } else { throw new IllegalArgumentException("Unsupported architecture [" + architecture + "]") } @@ -85,12 +89,14 @@ RUN curl --retry 8 -S -L \\ private static String buildPath(Architecture architecture, DockerBase base) { return 'build/' + (architecture == Architecture.ARM64 ? 'arm64-' : '') + + (architecture == Architecture.S390X ? 's390x-' : '') + 'docker' } private static String taskName(String prefix, Architecture architecture, DockerBase base, String suffix) { return prefix + (architecture == Architecture.ARM64 ? 'Arm64' : '') + + (architecture == Architecture.S390X ? 'S390x' : '') + suffix } @@ -127,6 +133,8 @@ void addCopyDockerContextTask(Architecture architecture, DockerBase base) { if (architecture == Architecture.ARM64) { from configurations.arm64DockerSource + } else if (architecture == Architecture.S390X) { + from configurations.s390xDockerSource } else { from configurations.dockerSource } diff --git a/distribution/docker/docker-s390x-export/build.gradle b/distribution/docker/docker-s390x-export/build.gradle new file mode 100644 index 0000000000000..3506c4e39c234 --- /dev/null +++ b/distribution/docker/docker-s390x-export/build.gradle @@ -0,0 +1,13 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +// This file is intentionally blank. All configuration of the +// export is done in the parent project. diff --git a/server/src/main/java/org/opensearch/bootstrap/SystemCallFilter.java b/server/src/main/java/org/opensearch/bootstrap/SystemCallFilter.java index f8baee06c4315..7d567d73851a9 100644 --- a/server/src/main/java/org/opensearch/bootstrap/SystemCallFilter.java +++ b/server/src/main/java/org/opensearch/bootstrap/SystemCallFilter.java @@ -259,6 +259,7 @@ static class Arch { Map m = new HashMap<>(); m.put("amd64", new Arch(0xC000003E, 0x3FFFFFFF, 57, 58, 59, 322, 317)); m.put("aarch64", new Arch(0xC00000B7, 0xFFFFFFFF, 1079, 1071, 221, 281, 277)); + m.put("s390x", new Arch(0x80000016, 0xFFFFFFFF, 2, 190, 11, 354, 348)); ARCHITECTURES = Collections.unmodifiableMap(m); } diff --git a/settings.gradle b/settings.gradle index 4c389b5490e7c..92e07cbb2e7fb 100644 --- a/settings.gradle +++ b/settings.gradle @@ -47,11 +47,13 @@ List projects = [ 'distribution:archives:freebsd-tar', 'distribution:archives:no-jdk-freebsd-tar', 'distribution:archives:linux-arm64-tar', + 'distribution:archives:linux-s390x-tar', 'distribution:archives:linux-tar', 'distribution:archives:no-jdk-linux-tar', 'distribution:docker', 'distribution:docker:docker-arm64-build-context', 'distribution:docker:docker-arm64-export', + 'distribution:docker:docker-s390x-export', 'distribution:docker:docker-build-context', 'distribution:docker:docker-export', 'distribution:packages:arm64-deb', From 54364a5d45ed0a20de42abb53cca8f33cfed88eb Mon Sep 17 00:00:00 2001 From: Suraj Singh Date: Thu, 8 Sep 2022 19:54:06 -0700 Subject: [PATCH 180/187] 2.3.0 release notes (#4457) (#4464) * 2.3.0 release notes Signed-off-by: Suraj Singh * Add changelog entry Signed-off-by: Suraj Singh Signed-off-by: Suraj Singh Signed-off-by: Suraj Singh --- CHANGELOG.md | 1 + .../opensearch.release-notes-2.3.0.md | 55 +++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 release-notes/opensearch.release-notes-2.3.0.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 75fde94ee7878..a10824a56af05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - BWC version 2.2.2 ([#4383](https://github.com/opensearch-project/OpenSearch/pull/4383)) - Support for labels on version bump PRs, skip label support for changelog verifier ([#4391](https://github.com/opensearch-project/OpenSearch/pull/4391)) - Update previous release bwc version to 2.4.0 ([#4455](https://github.com/opensearch-project/OpenSearch/pull/4455)) +- 2.3.0 release notes ([#4457](https://github.com/opensearch-project/OpenSearch/pull/4457)) ### Dependencies - Bumps `org.gradle.test-retry` from 1.4.0 to 1.4.1 diff --git a/release-notes/opensearch.release-notes-2.3.0.md b/release-notes/opensearch.release-notes-2.3.0.md new file mode 100644 index 0000000000000..1532ab31106f7 --- /dev/null +++ b/release-notes/opensearch.release-notes-2.3.0.md @@ -0,0 +1,55 @@ +## 2022-09-08 Version 2.3.0 Release Notes + +### Features/Enhancements +* [Backport to 2.x] [Segment Replication] - Update replicas to commit SegmentInfos instead of relying on segments_N from primary shards. ([#4450](https://github.com/opensearch-project/opensearch/pull/4450)) +* [Segment Replication] [Backport] Fix timeout issue by calculating time needed to process getSegmentFiles. ([#4434](https://github.com/opensearch-project/opensearch/pull/4434)) +* [Semgnet Replication] Update flaky testOnNewCheckpointFromNewPrimaryCancelOngoingReplication unit test ([#4414](https://github.com/opensearch-project/opensearch/pull/4414)) ([#4425](https://github.com/opensearch-project/opensearch/pull/4425)) +* [Segment Replication] Extend FileChunkWriter to allow cancel on transport client ([#4386](https://github.com/opensearch-project/opensearch/pull/4386)) ([#4424](https://github.com/opensearch-project/opensearch/pull/4424)) +* Segment Replication - Fix NoSuchFileException errors caused when computing metadata snapshot on primary shards. ([#4366](https://github.com/opensearch-project/opensearch/pull/4366)) ([#4422](https://github.com/opensearch-project/opensearch/pull/4422)) +* [Remote Store] Add index specific setting for remote repository ([#4253](https://github.com/opensearch-project/opensearch/pull/4253)) ([#4418](https://github.com/opensearch-project/opensearch/pull/4418)) +* [Segment Replication] Add check to cancel ongoing replication with old primary on onNewCheckpoint on replica ([#4363](https://github.com/opensearch-project/opensearch/pull/4363)) ([#4396](https://github.com/opensearch-project/opensearch/pull/4396)) +* [Segment Replication] Bump segment infos counter before commit during replica promotion ([#4365](https://github.com/opensearch-project/opensearch/pull/4365)) ([#4397](https://github.com/opensearch-project/opensearch/pull/4397)) +* Segment Replication - Implement segment replication event cancellation. ([#4225](https://github.com/opensearch-project/opensearch/pull/4225)) ([#4387](https://github.com/opensearch-project/opensearch/pull/4387)) +* [Backport 2.x] [Remote Store] Backport remote segment store changes ([#4380](https://github.com/opensearch-project/opensearch/pull/4380)) +* [Backport 2.x] Added timing data and more granular stages to SegmentReplicationState ([#4367](https://github.com/opensearch-project/opensearch/pull/4367)) +* [Backport 2.x] Support shard promotion with Segment Replication. ([#4135](https://github.com/opensearch-project/opensearch/pull/4135)) ([#4325](https://github.com/opensearch-project/opensearch/pull/4325)) +* [Segment Replication] Update PrimaryShardAllocator to prefer replicas with higher replication checkpoint ([#4041](https://github.com/opensearch-project/opensearch/pull/4041)) ([#4252](https://github.com/opensearch-project/opensearch/pull/4252)) +* [Backport 2.x] [Segment Replication] Backport all PR's containing remaining segment replication changes ([#4243](https://github.com/opensearch-project/opensearch/pull/4243)) +* [Backport 2.x] [Segment Replication] Backport PR's : #3525 #3533 #3540 #3943 #3963 From main branch ([#4181](https://github.com/opensearch-project/opensearch/pull/4181)) +* [Backport 2.x] [Segment Replication] Added source-side classes for orchestrating replication events. ([#4128](https://github.com/opensearch-project/opensearch/pull/4128)) + +### Bug Fixes +* [Bug]: gradle check failing with java heap OutOfMemoryError ([#4328](https://github.com/opensearch-project/opensearch/pull/4328)) ([#4442](https://github.com/opensearch-project/opensearch/pull/4442)) +* [Backport 2.x] Revert to Netty 4.1.79.Final ([#4432](https://github.com/opensearch-project/opensearch/pull/4432)) +* Bug fixes for dependabot changelog verifier ([#4364](https://github.com/opensearch-project/opensearch/pull/4364)) ([#4395](https://github.com/opensearch-project/opensearch/pull/4395)) +* [BUG] Create logs directory before running OpenSearch on Windows ([#4305](https://github.com/opensearch-project/opensearch/pull/4305)) ([#4335](https://github.com/opensearch-project/opensearch/pull/4335)) +* [BUG] Running "opensearch-service.bat start" and "opensearch-service.bat manager" ([#4289](https://github.com/opensearch-project/opensearch/pull/4289)) ([#4293](https://github.com/opensearch-project/opensearch/pull/4293)) +* [Backport 2.x] Do not fail replica shard due to primary closure ([#4309](https://github.com/opensearch-project/opensearch/pull/4309)) +* [Bug]: gradle check failing with java heap OutOfMemoryError ([#4150](https://github.com/opensearch-project/opensearch/pull/4150)) ([#4167](https://github.com/opensearch-project/opensearch/pull/4167)) +* OpenSearch crashes on closed client connection before search reply when total ops higher compared to expected ([#4143](https://github.com/opensearch-project/opensearch/pull/4143)) ([#4144](https://github.com/opensearch-project/opensearch/pull/4144)) + +### Infrastructure +* Add workflow for changelog verification ([#4085](https://github.com/opensearch-project/opensearch/pull/4085)) ([#4284](https://github.com/opensearch-project/opensearch/pull/4284)) +* Add 2.x version to CHANGELOG ([#4297](https://github.com/opensearch-project/opensearch/pull/4297)) ([#4303](https://github.com/opensearch-project/opensearch/pull/4303)) +* Update the head ref to changelog verifier ([#4296](https://github.com/opensearch-project/opensearch/pull/4296)) ([#4298](https://github.com/opensearch-project/opensearch/pull/4298)) +* Publish transport-netty4 module to central repository ([#4054](https://github.com/opensearch-project/opensearch/pull/4054)) ([#4078](https://github.com/opensearch-project/opensearch/pull/4078)) + +### Maintenance +* Add bwcVersion 1.3.6 to 2.x ([#4452](https://github.com/opensearch-project/opensearch/pull/4452)) +* [AUTO] [2.x] Added bwc version 2.2.2. ([#4385](https://github.com/opensearch-project/opensearch/pull/4385)) +* Update to Netty 4.1.80.Final ([#4359](https://github.com/opensearch-project/opensearch/pull/4359)) ([#4374](https://github.com/opensearch-project/opensearch/pull/4374)) +* Adding @dreamer-89 to Opensearch maintainers. ([#4342](https://github.com/opensearch-project/opensearch/pull/4342)) ([#4345](https://github.com/opensearch-project/opensearch/pull/4345)) +* [CVE] Update snakeyaml dependency ([#4341](https://github.com/opensearch-project/opensearch/pull/4341)) ([#4347](https://github.com/opensearch-project/opensearch/pull/4347)) +* Some dependency updates ([#4308](https://github.com/opensearch-project/opensearch/pull/4308)) ([#4311](https://github.com/opensearch-project/opensearch/pull/4311)) +* Added bwc version 2.2.1 ([#4193](https://github.com/opensearch-project/opensearch/pull/4193)) +* Update Gradle to 7.5.1 ([#4211](https://github.com/opensearch-project/opensearch/pull/4211)) ([#4213](https://github.com/opensearch-project/opensearch/pull/4213)) +* [Backport] Upgrade dependencies ([#4165](https://github.com/opensearch-project/opensearch/pull/4165)) +* Bumping 2.x to 2.3.0 ([#4098](https://github.com/opensearch-project/opensearch/pull/4098)) + +### Refactoring +* Refactored the src and test of GeoHashGrid and GeoTileGrid Aggregations on GeoPoint from server folder to geo module.([#4071](https://github.com/opensearch-project/opensearch/pull/4071)) ([#4072](https://github.com/opensearch-project/opensearch/pull/4072)) ([#4180](https://github.com/opensearch-project/opensearch/pull/4180)) ([#4281](https://github.com/opensearch-project/opensearch/pull/4281)) +* Update the head ref to changelog verifier ([#4296](https://github.com/opensearch-project/opensearch/pull/4296)) ([#4298](https://github.com/opensearch-project/opensearch/pull/4298)) +* [2.x] Restore using the class ClusterInfoRequest and ClusterInfoRequestBuilder from package 'org.opensearch.action.support.master.info' for subclasses ([#4307](https://github.com/opensearch-project/opensearch/pull/4307)) ([#4324](https://github.com/opensearch-project/opensearch/pull/4324)) +* Refactored the src and test of GeoHashGrid and GeoTileGrid Aggregations on GeoPoint from server folder to geo module.([#4071](https://github.com/opensearch-project/opensearch/pull/4071)) ([#4072](https://github.com/opensearch-project/opensearch/pull/4072)) ([#4180](https://github.com/opensearch-project/opensearch/pull/4180)) ([#4281](https://github.com/opensearch-project/opensearch/pull/4281)) +* Refactors the GeoBoundsAggregation for geo_point types from the core server to the geo module. ([#4179](https://github.com/opensearch-project/opensearch/pull/4179)) +* Backporting multiple 2.* release notes from main to the 2.x branch ([#4154](https://github.com/opensearch-project/opensearch/pull/4154)) From f245a3bd9434161dd4ec18b966c8ee19568c9350 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Fri, 9 Sep 2022 14:34:54 +0530 Subject: [PATCH 181/187] Fix controller tests Signed-off-by: Rishab Nahata --- .../DecommissionControllerTests.java | 87 +++++++++++++++---- 1 file changed, 72 insertions(+), 15 deletions(-) diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java index 95199cdf09487..3e1c5d36fb84e 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java @@ -8,6 +8,7 @@ package org.opensearch.cluster.decommission; +import org.hamcrest.Matchers; import org.junit.After; import org.junit.Before; import org.opensearch.OpenSearchTimeoutException; @@ -18,6 +19,8 @@ import org.opensearch.action.support.ActionFilters; import org.opensearch.cluster.ClusterName; import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.ClusterStateObserver; +import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.coordination.CoordinationMetadata; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.metadata.Metadata; @@ -48,8 +51,11 @@ import static java.util.Collections.emptySet; import static java.util.Collections.singletonMap; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.sameInstance; import static org.hamcrest.Matchers.startsWith; import static org.opensearch.cluster.ClusterState.builder; import static org.opensearch.cluster.OpenSearchAllocationTestCase.createAllocationService; @@ -58,23 +64,17 @@ public class DecommissionControllerTests extends OpenSearchTestCase { - private ThreadPool threadPool; - private ClusterService clusterService; + private static ThreadPool threadPool; + private static ClusterService clusterService; private TransportService transportService; private AllocationService allocationService; private DecommissionController decommissionController; private ClusterSettings clusterSettings; - @Override - public void setUp() throws Exception { - super.setUp(); - threadPool = new TestThreadPool("test", Settings.EMPTY); - clusterService = createClusterService(threadPool); - allocationService = createAllocationService(); - } - @Before public void setTransportServiceAndDefaultClusterState() { + threadPool = new TestThreadPool("test", Settings.EMPTY); + allocationService = createAllocationService(); ClusterState clusterState = ClusterState.builder(new ClusterName("test")).build(); logger.info("--> adding five nodes on same zone_1"); clusterState = addNodes(clusterState, "zone_1", "node1", "node2", "node3", "node4", "node5"); @@ -85,6 +85,7 @@ public void setTransportServiceAndDefaultClusterState() { clusterState = setLocalNodeAsClusterManagerNode(clusterState, "node1"); clusterState = setThreeNodesInVotingConfig(clusterState); final ClusterState.Builder builder = builder(clusterState); + clusterService = createClusterService(threadPool, clusterState.nodes().get("node1")); setState(clusterService, builder); final MockTransport transport = new MockTransport(); transportService = transport.createTransportService( @@ -130,7 +131,10 @@ public void shutdownThreadPoolAndClusterService() { } public void testAddNodesToVotingConfigExclusion() throws InterruptedException { - final CountDownLatch countDownLatch = new CountDownLatch(1); + final CountDownLatch countDownLatch = new CountDownLatch(2); + + ClusterStateObserver clusterStateObserver = new ClusterStateObserver(clusterService, null, logger, threadPool.getThreadContext()); + clusterStateObserver.waitForNextChange(new AdjustConfigurationForExclusions(countDownLatch)); Set nodesToRemoveFromVotingConfig = Collections.singleton(randomFrom("node1", "node6", "node11")); decommissionController.excludeDecommissionedNodesFromVotingConfig(nodesToRemoveFromVotingConfig, new ActionListener() { @Override @@ -145,7 +149,7 @@ public void onFailure(Exception e) { }); assertTrue(countDownLatch.await(30, TimeUnit.SECONDS)); clusterService.getClusterApplierService().state().getVotingConfigExclusions().forEach(vce -> { - assertTrue(nodesToRemoveFromVotingConfig.contains(vce.getNodeName())); + assertTrue(nodesToRemoveFromVotingConfig.contains(vce.getNodeId())); assertEquals(nodesToRemoveFromVotingConfig.size(), 1); }); } @@ -214,7 +218,7 @@ public void testTimesOut() throws InterruptedException { nodesToBeRemoved.add(clusterService.state().nodes().get("node15")); decommissionController.removeDecommissionedNodes( nodesToBeRemoved, - "unit-test", + "unit-test-timeout", TimeValue.timeValueMillis(2), new ActionListener() { @Override @@ -225,7 +229,7 @@ public void onResponse(Void unused) { @Override public void onFailure(Exception e) { assertThat(e, instanceOf(OpenSearchTimeoutException.class)); - assertThat(e.getMessage(), startsWith("timed out waiting for removal of decommissioned nodes")); + assertThat(e.getMessage(), containsString("waiting for removal of decommissioned nodes")); countDownLatch.countDown(); } } @@ -251,6 +255,7 @@ public void testSuccessfulDecommissionStatusMetadataUpdate() throws InterruptedE new ActionListener() { @Override public void onResponse(DecommissionStatus status) { + assertEquals(DecommissionStatus.SUCCESSFUL, status); countDownLatch.countDown(); } @@ -266,6 +271,58 @@ public void onFailure(Exception e) { assertEquals(decommissionAttributeMetadata.status(), DecommissionStatus.SUCCESSFUL); } + private static class AdjustConfigurationForExclusions implements ClusterStateObserver.Listener { + + final CountDownLatch doneLatch; + + AdjustConfigurationForExclusions(CountDownLatch latch) { + this.doneLatch = latch; + } + + @Override + public void onNewClusterState(ClusterState state) { + clusterService.getClusterManagerService().submitStateUpdateTask("reconfiguration", new ClusterStateUpdateTask() { + @Override + public ClusterState execute(ClusterState currentState) { + assertThat(currentState, sameInstance(state)); + final Set votingNodeIds = new HashSet<>(); + currentState.nodes().forEach(n -> votingNodeIds.add(n.getId())); + currentState.getVotingConfigExclusions().forEach(t -> votingNodeIds.remove(t.getNodeId())); + final CoordinationMetadata.VotingConfiguration votingConfiguration = new CoordinationMetadata.VotingConfiguration(votingNodeIds); + return builder(currentState).metadata( + Metadata.builder(currentState.metadata()) + .coordinationMetadata( + CoordinationMetadata.builder(currentState.coordinationMetadata()) + .lastAcceptedConfiguration(votingConfiguration) + .lastCommittedConfiguration(votingConfiguration) + .build() + ) + ).build(); + } + + @Override + public void onFailure(String source, Exception e) { + throw new AssertionError("unexpected failure", e); + } + + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + doneLatch.countDown(); + } + }); + } + + @Override + public void onClusterServiceClose() { + throw new AssertionError("unexpected close"); + } + + @Override + public void onTimeout(TimeValue timeout) { + throw new AssertionError("unexpected timeout"); + } + } + private ClusterState addNodes(ClusterState clusterState, String zone, String... nodeIds) { DiscoveryNodes.Builder nodeBuilder = DiscoveryNodes.builder(clusterState.nodes()); org.opensearch.common.collect.List.of(nodeIds).forEach(nodeId -> nodeBuilder.add(newNode(nodeId, singletonMap("zone", zone)))); @@ -300,7 +357,7 @@ private ClusterState setThreeNodesInVotingConfig(ClusterState clusterState) { } private static DiscoveryNode newNode(String nodeId, Map attributes) { - return new DiscoveryNode(nodeId, buildNewFakeTransportAddress(), attributes, CLUSTER_MANAGER_DATA_ROLE, Version.CURRENT); + return new DiscoveryNode(nodeId, nodeId, buildNewFakeTransportAddress(), attributes, CLUSTER_MANAGER_DATA_ROLE, Version.CURRENT); } final private static Set CLUSTER_MANAGER_DATA_ROLE = Collections.unmodifiableSet( From 193a7ecf5d0826c17fb42d4ccfad2a2582222578 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Fri, 9 Sep 2022 15:27:59 +0530 Subject: [PATCH 182/187] Fix Decommission Service test Signed-off-by: Rishab Nahata --- .../decommission/DecommissionController.java | 1 - .../decommission/DecommissionService.java | 3 +- .../DecommissionServiceTests.java | 72 +++++++++++-------- 3 files changed, 43 insertions(+), 33 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java index 244339b7ccd03..715235d7bff3b 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java @@ -253,7 +253,6 @@ public void onFailure(String source, Exception e) { public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { DecommissionAttributeMetadata decommissionAttributeMetadata = newState.metadata() .custom(DecommissionAttributeMetadata.TYPE); - logger.info("updated decommission status to [{}]", decommissionAttributeMetadata.status()); listener.onResponse(decommissionAttributeMetadata.status()); } }); diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 9e1c10c5167d6..1e47fd33ba043 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -269,6 +269,7 @@ private void failDecommissionedNodes(ClusterState state) { decommissionController.updateMetadataWithDecommissionStatus(DecommissionStatus.IN_PROGRESS, new ActionListener<>() { @Override public void onResponse(DecommissionStatus status) { + logger.info("updated the decommission status to [{}]", status.toString()); // execute nodes decommissioning decommissionController.removeDecommissionedNodes( filterNodesWithDecommissionAttribute(clusterService.getClusterApplierService().state(), decommissionAttribute, false), @@ -425,7 +426,7 @@ private ActionListener statusUpdateListener() { return new ActionListener() { @Override public void onResponse(DecommissionStatus status) { - logger.info("updated the status to [{}]", status.toString()); + logger.info("updated the decommission status to [{}]", status.toString()); } @Override diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java index 63e285e2dab36..837a1f0d8b275 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java @@ -15,6 +15,7 @@ import org.opensearch.action.ActionListener; import org.opensearch.cluster.ClusterName; import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.ClusterStateObserver; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; import org.opensearch.cluster.coordination.CoordinationMetadata; import org.opensearch.cluster.metadata.Metadata; @@ -26,6 +27,7 @@ import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Settings; +import org.opensearch.common.unit.TimeValue; import org.opensearch.test.OpenSearchTestCase; import org.opensearch.test.transport.MockTransport; import org.opensearch.threadpool.TestThreadPool; @@ -36,6 +38,8 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import static java.util.Collections.emptySet; import static java.util.Collections.singletonMap; @@ -116,18 +120,18 @@ public void shutdownThreadPoolAndClusterService() { } @SuppressWarnings("unchecked") - public void testDecommissioningNotInitiatedForInvalidAttributeName() { + public void testDecommissioningNotStartedForInvalidAttributeName() { DecommissionAttribute decommissionAttribute = new DecommissionAttribute("rack", "rack-a"); ActionListener listener = mock(ActionListener.class); DecommissioningFailedException e = expectThrows( DecommissioningFailedException.class, - () -> { decommissionService.startDecommissionAction(decommissionAttribute, listener); } + () -> decommissionService.startDecommissionAction(decommissionAttribute, listener) ); assertThat(e.getMessage(), Matchers.endsWith("invalid awareness attribute requested for decommissioning")); } @SuppressWarnings("unchecked") - public void testDecommissioningNotInitiatedForInvalidAttributeValue() { + public void testDecommissioningNotStartedForInvalidAttributeValue() { DecommissionAttribute decommissionAttribute = new DecommissionAttribute("zone", "random"); ActionListener listener = mock(ActionListener.class); DecommissioningFailedException e = expectThrows( @@ -143,10 +147,12 @@ public void testDecommissioningNotInitiatedForInvalidAttributeValue() { } @SuppressWarnings("unchecked") - public void testDecommissioningNotInitiatedWhenAlreadyDecommissioned() { + public void testDecommissioningFailedWhenAnotherAttributeDecommissioningSuccessful() throws InterruptedException { + final CountDownLatch countDownLatch = new CountDownLatch(1); + DecommissionStatus oldStatus = randomFrom(DecommissionStatus.SUCCESSFUL, DecommissionStatus.IN_PROGRESS, DecommissionStatus.INIT); DecommissionAttributeMetadata oldMetadata = new DecommissionAttributeMetadata( new DecommissionAttribute("zone", "zone_1"), - DecommissionStatus.IN_PROGRESS + oldStatus ); final ClusterState.Builder builder = builder(clusterService.state()); setState( @@ -155,32 +161,36 @@ public void testDecommissioningNotInitiatedWhenAlreadyDecommissioned() { Metadata.builder(clusterService.state().metadata()).putCustom(DecommissionAttributeMetadata.TYPE, oldMetadata).build() ) ); - ActionListener listener = mock(ActionListener.class); - DecommissioningFailedException e = expectThrows( - DecommissioningFailedException.class, - () -> { decommissionService.startDecommissionAction(new DecommissionAttribute("zone", "zone_2"), listener); } - ); - assertThat(e.getMessage(), Matchers.endsWith("another request for decommission is in flight, will not process this request")); - } - - @SuppressWarnings("unchecked") - public void testDecommissioningInitiatedWhenEnoughClusterManagerNodes() { - ActionListener listener = mock(ActionListener.class); - decommissionService.startDecommissionAction(new DecommissionAttribute("zone", "zone_3"), listener); - } - - @SuppressWarnings("unchecked") - public void testDecommissioningNotInitiatedWhenNotEnoughClusterManagerNodes() { - ClusterState state = clusterService.state(); - // shrink voting config - state = setNodesInVotingConfig(state, state.nodes().get("node1"), state.nodes().get("node11")); - setState(clusterService, state); - ActionListener listener = mock(ActionListener.class); - DecommissioningFailedException e = expectThrows( - DecommissioningFailedException.class, - () -> { decommissionService.startDecommissionAction(new DecommissionAttribute("zone", "zone_3"), listener); } - ); - assertThat(e.getMessage(), Matchers.endsWith("cannot proceed with decommission request as cluster might go into quorum loss")); + ActionListener listener = new ActionListener() { + @Override + public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { + fail("on response shouldn't have been called"); + } + + @Override + public void onFailure(Exception e) { + assertTrue(e instanceof DecommissioningFailedException); + if (oldStatus.equals(DecommissionStatus.SUCCESSFUL)) { + assertThat( + e.getMessage(), + Matchers.endsWith( + "already successfully decommissioned, recommission before triggering another decommission" + ) + ); + } + else { + assertThat( + e.getMessage(), + Matchers.endsWith( + "is in progress, cannot process this request" + ) + ); + } + countDownLatch.countDown(); + } + }; + decommissionService.startDecommissionAction(new DecommissionAttribute("zone", "zone_2"), listener); + assertTrue(countDownLatch.await(30, TimeUnit.SECONDS)); } private ClusterState addDataNodes(ClusterState clusterState, String zone, String... nodeIds) { From 39867e30db21f67bf6359977438365b0f1562204 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Fri, 9 Sep 2022 15:34:23 +0530 Subject: [PATCH 183/187] Fix spotless check Signed-off-by: Rishab Nahata --- .../DecommissionControllerTests.java | 7 +++---- .../decommission/DecommissionServiceTests.java | 18 ++++-------------- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java index 3e1c5d36fb84e..4b85fa39a91e1 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionControllerTests.java @@ -8,7 +8,6 @@ package org.opensearch.cluster.decommission; -import org.hamcrest.Matchers; import org.junit.After; import org.junit.Before; import org.opensearch.OpenSearchTimeoutException; @@ -53,10 +52,8 @@ import static java.util.Collections.singletonMap; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; -import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.sameInstance; -import static org.hamcrest.Matchers.startsWith; import static org.opensearch.cluster.ClusterState.builder; import static org.opensearch.cluster.OpenSearchAllocationTestCase.createAllocationService; import static org.opensearch.test.ClusterServiceUtils.createClusterService; @@ -288,7 +285,9 @@ public ClusterState execute(ClusterState currentState) { final Set votingNodeIds = new HashSet<>(); currentState.nodes().forEach(n -> votingNodeIds.add(n.getId())); currentState.getVotingConfigExclusions().forEach(t -> votingNodeIds.remove(t.getNodeId())); - final CoordinationMetadata.VotingConfiguration votingConfiguration = new CoordinationMetadata.VotingConfiguration(votingNodeIds); + final CoordinationMetadata.VotingConfiguration votingConfiguration = new CoordinationMetadata.VotingConfiguration( + votingNodeIds + ); return builder(currentState).metadata( Metadata.builder(currentState.metadata()) .coordinationMetadata( diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java index 837a1f0d8b275..6e40b608ccc7a 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java @@ -15,7 +15,6 @@ import org.opensearch.action.ActionListener; import org.opensearch.cluster.ClusterName; import org.opensearch.cluster.ClusterState; -import org.opensearch.cluster.ClusterStateObserver; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; import org.opensearch.cluster.coordination.CoordinationMetadata; import org.opensearch.cluster.metadata.Metadata; @@ -27,7 +26,6 @@ import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.TimeValue; import org.opensearch.test.OpenSearchTestCase; import org.opensearch.test.transport.MockTransport; import org.opensearch.threadpool.TestThreadPool; @@ -152,7 +150,7 @@ public void testDecommissioningFailedWhenAnotherAttributeDecommissioningSuccessf DecommissionStatus oldStatus = randomFrom(DecommissionStatus.SUCCESSFUL, DecommissionStatus.IN_PROGRESS, DecommissionStatus.INIT); DecommissionAttributeMetadata oldMetadata = new DecommissionAttributeMetadata( new DecommissionAttribute("zone", "zone_1"), - oldStatus + oldStatus ); final ClusterState.Builder builder = builder(clusterService.state()); setState( @@ -173,18 +171,10 @@ public void onFailure(Exception e) { if (oldStatus.equals(DecommissionStatus.SUCCESSFUL)) { assertThat( e.getMessage(), - Matchers.endsWith( - "already successfully decommissioned, recommission before triggering another decommission" - ) - ); - } - else { - assertThat( - e.getMessage(), - Matchers.endsWith( - "is in progress, cannot process this request" - ) + Matchers.endsWith("already successfully decommissioned, recommission before triggering another decommission") ); + } else { + assertThat(e.getMessage(), Matchers.endsWith("is in progress, cannot process this request")); } countDownLatch.countDown(); } From 054cb5a0757043d37eb3ffc3e18218e9ae74420c Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Fri, 9 Sep 2022 16:04:57 +0530 Subject: [PATCH 184/187] Empty-Commit Signed-off-by: Rishab Nahata From 7b212af994be56299816ae343751f23390c8e1ea Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Mon, 12 Sep 2022 10:50:31 +0530 Subject: [PATCH 185/187] listener fix --- .../cluster/decommission/DecommissionService.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 1e47fd33ba043..89df75c8b73fc 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -12,6 +12,7 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; import org.opensearch.action.ActionListener; +import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionResponse; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.NotClusterManagerException; @@ -117,7 +118,7 @@ private void setForcedAwarenessAttributes(Settings forceSettings) { */ public synchronized void startDecommissionAction( final DecommissionAttribute decommissionAttribute, - final ActionListener listener + final ActionListener listener ) { // validates if correct awareness attributes and forced awareness attribute set to the cluster before starting action validateAwarenessAttribute(decommissionAttribute, awarenessAttributes, forcedAwarenessAttributes); @@ -170,7 +171,7 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS private synchronized void decommissionClusterManagerNodes( final DecommissionAttribute decommissionAttribute, - ActionListener listener + ActionListener listener ) { ClusterState state = clusterService.getClusterApplierService().state(); Set clusterManagerNodesToBeDecommissioned = filterNodesWithDecommissionAttribute(state, decommissionAttribute, true); @@ -199,7 +200,7 @@ public void onResponse(Void unused) { logger.info("will attempt to fail decommissioned nodes as local node is eligible to process the request"); // we are good here to send the response now as the request is processed by an eligible active leader // and to-be-decommissioned cluster manager is no more part of Voting Configuration - listener.onResponse(new ClusterStateUpdateResponse(true)); + listener.onResponse(new DecommissionResponse(true)); failDecommissionedNodes(clusterService.getClusterApplierService().state()); } else { // explicitly calling listener.onFailure with NotClusterManagerException as we can certainly say that From ea9a18405d0eee890e23267ea86a5d406c999544 Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Mon, 12 Sep 2022 10:54:45 +0530 Subject: [PATCH 186/187] fix --- .../cluster/decommission/DecommissionServiceTests.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java index 6e40b608ccc7a..197bf57cdaa67 100644 --- a/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java +++ b/server/src/test/java/org/opensearch/cluster/decommission/DecommissionServiceTests.java @@ -13,6 +13,7 @@ import org.junit.Before; import org.opensearch.Version; import org.opensearch.action.ActionListener; +import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionResponse; import org.opensearch.cluster.ClusterName; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; @@ -120,7 +121,7 @@ public void shutdownThreadPoolAndClusterService() { @SuppressWarnings("unchecked") public void testDecommissioningNotStartedForInvalidAttributeName() { DecommissionAttribute decommissionAttribute = new DecommissionAttribute("rack", "rack-a"); - ActionListener listener = mock(ActionListener.class); + ActionListener listener = mock(ActionListener.class); DecommissioningFailedException e = expectThrows( DecommissioningFailedException.class, () -> decommissionService.startDecommissionAction(decommissionAttribute, listener) @@ -131,7 +132,7 @@ public void testDecommissioningNotStartedForInvalidAttributeName() { @SuppressWarnings("unchecked") public void testDecommissioningNotStartedForInvalidAttributeValue() { DecommissionAttribute decommissionAttribute = new DecommissionAttribute("zone", "random"); - ActionListener listener = mock(ActionListener.class); + ActionListener listener = mock(ActionListener.class); DecommissioningFailedException e = expectThrows( DecommissioningFailedException.class, () -> { decommissionService.startDecommissionAction(decommissionAttribute, listener); } @@ -159,9 +160,9 @@ public void testDecommissioningFailedWhenAnotherAttributeDecommissioningSuccessf Metadata.builder(clusterService.state().metadata()).putCustom(DecommissionAttributeMetadata.TYPE, oldMetadata).build() ) ); - ActionListener listener = new ActionListener() { + ActionListener listener = new ActionListener() { @Override - public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { + public void onResponse(DecommissionResponse clusterStateUpdateResponse) { fail("on response shouldn't have been called"); } From b0ae398e28db05e92381d79c1b2d0d77ba849c6e Mon Sep 17 00:00:00 2001 From: Rishab Nahata Date: Mon, 12 Sep 2022 10:58:35 +0530 Subject: [PATCH 187/187] Delete decommission --- .../TransportDeleteDecommissionAction.java | 1 + .../decommission/DecommissionService.java | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/TransportDeleteDecommissionAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/TransportDeleteDecommissionAction.java index 34860f09ef005..8db770afc720c 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/TransportDeleteDecommissionAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/TransportDeleteDecommissionAction.java @@ -89,6 +89,7 @@ public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { @Override public void onFailure(Exception e) { logger.error("Recommission failed with exception " + e.getMessage()); + listener.onFailure(e); } }); diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java index 89df75c8b73fc..5b04c97759b94 100644 --- a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -436,4 +436,34 @@ public void onFailure(Exception e) { } }; } + + public void clearDecommissionStatus(final ActionListener listener) { + clusterService.submitStateUpdateTask("delete_decommission_state", new ClusterStateUpdateTask(Priority.URGENT) { + @Override + public ClusterState execute(ClusterState currentState) { + return deleteDecommissionAttribute(currentState); + } + + @Override + public void onFailure(String source, Exception e) { + logger.error(() -> new ParameterizedMessage("Failed to clear decommission attribute."), e); + listener.onFailure(e); + } + + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + // Once the cluster state is processed we can try to recommission nodes by setting the weights for the zone. + // TODO Set the weights for the recommissioning zone. + listener.onResponse(new ClusterStateUpdateResponse(true)); + } + }); + } + + ClusterState deleteDecommissionAttribute(final ClusterState currentState) { + logger.info("Delete decommission request received"); + Metadata metadata = currentState.metadata(); + Metadata.Builder mdBuilder = Metadata.builder(metadata); + mdBuilder.removeCustom(DecommissionAttributeMetadata.TYPE); + return ClusterState.builder(currentState).metadata(mdBuilder).build(); + } }