You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@solr.apache.org by is...@apache.org on 2022/01/03 08:08:21 UTC
[solr] branch jira/SOLR15694 updated (6e97249 -> 60c4170)
This is an automated email from the ASF dual-hosted git repository.
ishan pushed a change to branch jira/SOLR15694
in repository https://gitbox.apache.org/repos/asf/solr.git.
omit 6e97249 cleanup and more test assertions
omit f0b238f addressed review comments
omit 5d94773 Addressing review feedback
omit 974ef77 Merge branch 'jira/SOLR15694' of https://gitbox.apache.org/repos/asf/solr into jira/SOLR15694
omit 2d49e2e Fix typo in reg guide
omit 1b6db9b rearranged API paths
omit 9bff65e Ref guide fixes
omit 360e9ce Merge branch 'jira/SOLR15694' of https://gitbox.apache.org/repos/asf/solr into jira/SOLR15694
omit 0f3ff02 Adding Roles API to ref guide
omit 1902f5f Fixing precommit
omit 5339e74 Merge branch 'jira/SOLR15694' of https://gitbox.apache.org/repos/asf/solr into jira/SOLR15694
omit 24e5ba7 SOLR-15694: Ref guide changes
omit fe421b4 removed unnecessary changes
omit af96956 removed unnecessary changes
omit 2a5485d removed unnecessary changes
omit 5e1bfa6 removed nocommit
omit 41e85ad clanup
omit da9eb9e supportedroles and overseer:disallowed supported
omit b49e976 Adding defaultIfAbsent, minor formatting fixes
omit e389530 new format
omit 104cf95 unused import
omit 85bc85e refactor
omit a16108b Multiple roles supported per node
omit 75cb680 tests were failing intermittently
omit 01639c4 added test
omit b2c7799 added APIs
omit 69d8313 use a separate node
omit c1da50b suppress warnings
omit 7ac8a62 cleanup
omit 6e353a8 license header
omit 4b082eb Node role impl
add 93c36a8 fixed esri link (#400)
add 67c55b5 LUCENE-9229 fixed 3 javadoc links 1) google guave, 2&3) Two links relocated away from "aggregateknowledge" (#402)
add 2d2d4c4 SOLR-10529 Revert change to solrconfig for techproducts example as it introduced test failures
add 79f3d18 SOLR-15663: JsonFaceting benchmark should flush by doc count for better reproducible behavior. (#388)
add 5c6d12d Fix githubPRs.py to query the new solr repo (#405)
add 1c80803 SOLR-15660: Remove universal 10 second test thread leak linger. (#377)
add 401da35 SOLR-15711: Fix GCSRepository to follow createDirectory API contract (#373)
add 2e5f89a SOLR-15783: Prevent Logging MDC values from leaking between request threads, and set 'trace_id' in MDC as soon as it's available
add 7646353 SOLR-15793: ref-guide jrubyPrepare fails due to gems trying to compile native code (#412)
add aea698b SOLR-15790: SearchHandler now includes the value in the Logging MDC for the duration of the request, allowing custom logging configurations to include it.
add 9ee30f1 SOLR-15791: Remove remaining <admin> clauses from solrconfigs (#411)
add f088857 Switching a PRS collection from true -> false -> true results in INACTIVE replicas (#413)
add dbdd2d4 SOLR-15635: don't repeat close hooks if SRI cleared twice due to using MDCThreadPool (#376)
add 0f9e482 SOLR-15774: Avoid weird off-by-one errors with Angular's 'chosen' select box directive for the security and schema-designer screens in Admin UI (#404)
add 81d3478 SOLR-15536: Rewrite /c/<coll>/shards v2 APIs using annotations (#218)
add 9b1f8dd SOLR-15792: Rewrite shardname APIs using annotation framework (#415)
add 8479055 SOLR-14781: Delete unused classes (#330)
add d18e247 SOLR-15619: add Long support for @JsonProperty annotations (#285)
add c2dcba1 SOLR-15635: move to 8.11.1 in CHANGES.txt (#417)
add ccb7d4d DOAP changes for release 8.11.0
add bfee656 SOLR-15784 Remove SolrJ dep on commons-io (#407)
add 111bd91 SOLR-15785: Load custom endpoints from solr/lib (#408)
add 70a19f9 SOLR-15795: Fix REPLACENODE to not use source node (#414)
add be96b2b a very embarrasing misspelling of the name of a contributor
add c214e07 SOLR-15376: Accept "Long" values for CollectionAdminRequest.CreateTimeRoutedAlias.setMaxFutureMs to support durations greater than ~25 days. (#202)
add a8290de SOLR-13138: Remove deprecated spatial (#382)
add 1ba86a8 SOLR-15806: expand UpdateLogCloudTest coverage w.r.t. Ephemeral vs. Standard DirectoryFactory use (#421)
add 46d2ad2 SOLR-15804 Allow content-types with ;charset in ShowFileRequestHandler (#418)
add e90f50f SOLR-11623 Every request handler in Solr implement PermissionNameProvider (#372)
add 5866f80 SOLR-15774: Move changes entry to 8.11.1 section
add 3ec2fa0 SOLR-15808: The jattach tool for Docker image is now installed with apt (#423)
add cee22de SOLR-15700: Make include paths in bin/solr consistent (#358)
add 55f6fbf Revert "SOLR-11623 Every request handler in Solr implement PermissionNameProvider (#372)"
add 5f34dbb SOLR-15182: Remove orphaned apispec file
add 058f2b6 Logging: SpellCheckComponent shouldn't log at info level (#422)
add 0cb23c7 SOLR-15427: Nested docs: [child limit=...] now defaults to -1 (#144)
add bf39765 SOLR-15182: replica API conversion to annotations (#429)
add fc5fb26 Ref Guide: 8.11 upgrade notes
new fa036ab Node role impl
new 277e415 license header
new d694418 cleanup
new 907d1f1 suppress warnings
new 16e3ec8 use a separate node
new 75d8410 added APIs
new 71c11f9 added test
new d4d98e5 tests were failing intermittently
new 84982ef Multiple roles supported per node
new fa8059e refactor
new 0587057 unused import
new 2716b06 new format
new 072f659 Adding defaultIfAbsent, minor formatting fixes
new ff26ae5 supportedroles and overseer:disallowed supported
new e4e24dc SOLR-15694: Ref guide changes
new c941fd8 clanup
new 4fb3713 removed nocommit
new 525ff87 removed unnecessary changes
new 1cf58d7 removed unnecessary changes
new 37b216c removed unnecessary changes
new 6ffba2a Adding Roles API to ref guide
new 3c17462 Fixing precommit
new a501b77 Ref guide fixes
new 7ec11d5 Fix typo in reg guide
new 483f984 rearranged API paths
new 50f40a1 Addressing review feedback
new 984edbf addressed review comments
new 60c4170 cleanup and more test assertions
This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version. This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:
* -- * -- B -- O -- O -- O (6e97249)
\
N -- N -- N refs/heads/jira/SOLR15694 (60c4170)
You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.
Any revisions marked "omit" are not gone; other references still
refer to them. Any revisions marked "discard" are gone forever.
The 28 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
dev-tools/doap/solr.rdf | 7 +
dev-tools/scripts/githubPRs.py | 10 +-
gradle/validation/forbidden-apis.gradle | 23 +-
gradle/validation/forbidden-apis/defaults.all.txt | 6 +
solr/CHANGES.txt | 43 +-
.../configs/cloud-minimal/conf/solrconfig.xml | 3 +-
.../solr/bench/MiniClusterBenchStateTest.java | 2 +
solr/bin/solr | 6 +-
.../apache/solr/schema/TestICUCollationField.java | 7 +-
.../schema/TestICUCollationFieldDocValues.java | 8 +-
.../analytics/function/reduction/MeanFunction.java | 7 -
.../analytics/function/reduction/SumFunction.java | 7 -
.../solr/analytics/util/OldAnalyticsParams.java | 34 --
.../solr/analytics/facet/PivotFacetTest.java | 2 +
.../legacy/facet/LegacyFieldFacetCloudTest.java | 2 +
.../legacy/facet/LegacyRangeFacetCloudTest.java | 3 +-
.../util/OldAnalyticsRequestConverterUnitTest.java | 2 +
.../solr/collection1/conf/solrconfig.xml | 11 +-
.../org/apache/solr/gcs/GCSBackupRepository.java | 21 +-
.../apache/solr/gcs/GCSIncrementalBackupTest.java | 2 +
.../apache/solr/jaeger/TestJaegerConfigurator.java | 2 +
...penNLPLangDetectUpdateProcessorFactoryTest.java | 2 +
.../org/apache/solr/ltr/TestLTROnSolrCloud.java | 2 +
.../prometheus/scraper/SolrCloudScraperTest.java | 2 +
.../apache/solr/s3/S3IncrementalBackupTest.java | 7 +-
.../apache/solr/api/ContainerPluginsRegistry.java | 6 +-
.../apache/solr/cloud/api/collections/Assign.java | 28 +-
.../solr/cloud/api/collections/ReplaceNodeCmd.java | 151 ++---
.../java/org/apache/solr/core/CoreContainer.java | 1 +
.../org/apache/solr/filestore/PackageStoreAPI.java | 14 +-
.../org/apache/solr/handler/CollectionsAPI.java | 10 +-
.../apache/solr/handler/ContentStreamLoader.java | 49 --
.../solr/handler/admin/BaseHandlerApiSupport.java | 4 +-
.../solr/handler/admin/CollectionHandlerApi.java | 77 ---
.../solr/handler/admin/CollectionsHandler.java | 17 +-
.../solr/handler/admin/CoreAdminHandlerApi.java | 4 +-
.../solr/handler/admin/ShowFileRequestHandler.java | 10 +-
...dReplicaPropertyAPI.java => AddReplicaAPI.java} | 42 +-
.../{MigrateDocsAPI.java => CreateShardAPI.java} | 57 +-
...lectionStatusAPI.java => DeleteReplicaAPI.java} | 40 +-
...ollectionStatusAPI.java => DeleteShardAPI.java} | 46 +-
.../solr/handler/admin/api/ForceLeaderAPI.java | 68 +++
.../handler/admin/api/ModifyCollectionAPI.java | 10 +-
.../{MigrateDocsAPI.java => SplitShardAPI.java} | 49 +-
.../solr/handler/admin/api/SyncShardAPI.java | 68 +++
.../org/apache/solr/handler/api/ApiRegistrar.java | 11 +
.../org/apache/solr/handler/api/V2ApiUtils.java} | 19 +-
.../solr/handler/component/SearchHandler.java | 16 +-
.../handler/component/SpellCheckComponent.java | 6 +-
.../java/org/apache/solr/legacy/BBoxStrategy.java | 3 +-
.../org/apache/solr/legacy/BBoxValueSource.java | 1 +
.../apache/solr/legacy/DistanceValueSource.java | 120 ----
.../apache/solr/legacy/PointVectorStrategy.java | 289 ----------
.../java/org/apache/solr/logging/MDCSnapshot.java | 58 ++
.../org/apache/solr/request/SolrRequestInfo.java | 70 ++-
.../apache/solr/response/RawResponseWriter.java | 5 +-
.../transform/ChildDocTransformerFactory.java | 22 +-
.../java/org/apache/solr/schema/GeoHashField.java | 103 ----
.../java/org/apache/solr/schema/LatLonType.java | 606 ---------------------
.../src/java/org/apache/solr/schema/PointType.java | 4 +-
.../solr/schema/SpatialPointVectorFieldType.java | 106 ----
.../org/apache/solr/schema/SpatialQueryable.java | 2 +-
.../SpatialTermQueryPrefixTreeFieldType.java | 33 --
.../solr/search/SpatialFilterQParserPlugin.java | 8 +-
.../apache/solr/search/facet/FacetFieldMerger.java | 42 --
.../distance/GeoDistValueSourceParser.java | 2 +-
.../search/function/distance/GeohashFunction.java | 1 +
.../distance/GeohashHaversineFunction.java | 3 +-
.../solr/search/stats/CachedSearcherStats.java | 22 -
.../java/org/apache/solr/servlet/HttpSolrCall.java | 7 +-
.../apache/solr/servlet/SolrDispatchFilter.java | 11 +-
.../org/apache/solr/update/SolrCmdDistributor.java | 4 -
.../HTMLStripFieldUpdateProcessorFactory.java | 2 +-
.../java/org/apache/solr/util/RedactionUtils.java | 55 --
.../src/java/org/apache/solr/util/hll/HLL.java | 6 +-
.../solr/collection1/conf/schema-hash.xml | 28 -
.../solr/collection1/conf/schema-minhash.xml | 28 -
.../solr/collection1/conf/schema-rest.xml | 15 -
.../solr/collection1/conf/schema-spatial.xml | 23 +-
.../solr/collection1/conf/schema-sql.xml | 28 -
.../test-files/solr/collection1/conf/schema.xml | 21 +-
.../test-files/solr/collection1/conf/schema11.xml | 17 -
.../test-files/solr/collection1/conf/schema12.xml | 22 +-
.../test-files/solr/collection1/conf/schema15.xml | 18 +-
.../solr/collection1/conf/schema_latest.xml | 35 +-
.../solr/collection1/conf/schemasurround.xml | 17 -
.../conf/solrconfig-analytics-query.xml | 6 -
.../conf/solrconfig-collapseqparser.xml | 6 -
.../conf/solrconfig-components-name.xml | 7 +-
.../conf/solrconfig-doctransformers.xml | 6 -
.../solr/collection1/conf/solrconfig-elevate.xml | 11 +-
.../solr/collection1/conf/solrconfig-hash.xml | 6 -
.../solr/collection1/conf/solrconfig-minhash.xml | 30 +-
.../collection1/conf/solrconfig-plugcollector.xml | 5 -
.../solr/collection1/conf/solrconfig-sql.xml | 6 -
.../solr/collection1/conf/solrconfig-test-misc.xml | 8 +-
.../solr/collection1/conf/solrconfig.xml | 34 +-
.../solr/configsets/cloud-dynamic/conf/schema.xml | 14 +-
.../solr/configsets/doc-expiry/conf/schema.xml | 18 -
.../test-files/solr/configsets/sql/conf/schema.xml | 15 -
.../src/test-files/solr/crazy-path-to-config.xml | 5 -
.../src/test/org/apache/hadoop/fs/HardLink.java | 14 -
.../test/org/apache/solr/TestCrossCoreJoin.java | 27 +-
.../test/org/apache/solr/TestGroupingSearch.java | 14 +-
.../apache/solr/cloud/CollectionsAPISolrJTest.java | 2 -
.../cloud/HttpPartitionWithTlogReplicasTest.java | 2 +
.../org/apache/solr/cloud/ReplaceNodeTest.java | 73 ++-
.../apache/solr/cloud/TestCloudConsistency.java | 2 +
.../solr/cloud/TestLeaderElectionZkExpiry.java | 8 +-
.../org/apache/solr/cloud/TestStressLiveNodes.java | 2 +
.../HdfsCloudIncrementalBackupTest.java | 8 +-
.../collections/TestHdfsCloudBackupRestore.java | 2 +
.../solr/cloud/hdfs/HDFSCollectionsAPITest.java | 2 +
.../cloud/hdfs/HdfsBasicDistributedZk2Test.java | 2 +
.../cloud/hdfs/HdfsBasicDistributedZkTest.java | 2 +
.../hdfs/HdfsChaosMonkeyNothingIsSafeTest.java | 2 +
.../cloud/hdfs/HdfsChaosMonkeySafeLeaderTest.java | 2 +
.../apache/solr/cloud/hdfs/HdfsNNFailoverTest.java | 2 +
.../solr/cloud/hdfs/HdfsRecoverLeaseTest.java | 2 +
.../apache/solr/cloud/hdfs/HdfsRecoveryZkTest.java | 2 +
.../cloud/hdfs/HdfsRestartWhileUpdatingTest.java | 2 +
.../apache/solr/cloud/hdfs/HdfsSyncSliceTest.java | 2 +
.../org/apache/solr/cloud/hdfs/HdfsTestUtil.java | 2 +
.../apache/solr/cloud/hdfs/HdfsThreadLeakTest.java | 2 +
.../HdfsTlogReplayBufferedWhileIndexingTest.java | 2 +
.../cloud/hdfs/HdfsUnloadDistributedZkTest.java | 2 +
.../hdfs/HdfsWriteToMultipleCollectionsTest.java | 2 +
.../org/apache/solr/cloud/hdfs/StressHdfsTest.java | 2 +
.../cluster/events/ClusterEventProducerTest.java | 4 +-
.../solr/core/BlobRepositoryMockingTest.java | 4 +-
.../solr/core/BlobStoreTestRequestHandler.java | 62 ---
.../apache/solr/core/HdfsDirectoryFactoryTest.java | 2 +
.../test/org/apache/solr/core/MockInfoBean.java | 65 ---
.../src/test/org/apache/solr/core/TestConfig.java | 30 +-
.../HdfsBackupRepositoryIntegrationTest.java | 2 +
.../org/apache/solr/handler/TestBlobHandler.java | 2 +
.../solr/handler/TestHdfsBackupRestoreCore.java | 2 +
.../org/apache/solr/handler/TestRequestId.java | 103 ++++
.../solr/handler/admin/DaemonStreamApiTest.java | 2 +
.../solr/handler/admin/MBeansHandlerTest.java | 3 +
.../handler/admin/ShowFileRequestHandlerTest.java | 7 +
.../solr/handler/admin/TestApiFramework.java | 5 +-
.../solr/handler/admin/TestCollectionAPIs.java | 30 +-
.../admin/api/V2CollectionAPIMappingTest.java | 2 +-
.../handler/admin/api/V2ShardsAPIMappingTest.java | 291 ++++++++++
.../DistributedQueryComponentCustomSortTest.java | 2 +
.../solr/handler/component/UpdateLogCloudTest.java | 20 +-
.../apache/solr/request/TestRemoteStreaming.java | 2 +-
.../apache/solr/request/TestSolrRequestInfo.java | 80 +++
.../solr/response/TestGeoJSONResponseWriter.java | 9 +-
.../transform/TestSubQueryTransformerDistrib.java | 7 +-
.../rest/schema/TestFieldCollectionResource.java | 24 +-
.../org/apache/solr/search/QueryEqualityTest.java | 40 +-
.../org/apache/solr/search/ReturnFieldsTest.java | 22 +-
.../org/apache/solr/search/SpatialFilterTest.java | 21 +-
.../org/apache/solr/search/TestSolr4Spatial.java | 5 +-
.../apache/solr/search/TestValueSourceCache.java | 6 +-
.../function/distance/DistanceFunctionTest.java | 80 ++-
.../apache/solr/store/hdfs/HdfsDirectoryTest.java | 2 +
.../AbstractAtomicUpdatesMultivalueTestBase.java | 6 +-
.../TimeRoutedAliasUpdateProcessorTest.java | 43 ++
.../solr/util/TestSolrJacksonAnnotation.java | 6 +
solr/docker/templates/Dockerfile.body.template | 7 +-
.../conf/solrconfig.xml | 10 -
solr/solr-ref-guide/build.gradle | 3 +
solr/solr-ref-guide/src/document-transformers.adoc | 4 +-
.../src/field-types-included-with-solr.adoc | 2 -
.../src/partial-document-updates.adoc | 2 +-
solr/solr-ref-guide/src/solr-upgrade-notes.adoc | 39 +-
solr/solr-ref-guide/src/spatial-search.adoc | 16 +-
solr/solrj/build.gradle | 2 +-
.../solr/client/solrj/impl/Http2SolrClient.java | 8 +-
.../solr/client/solrj/impl/HttpSolrClient.java | 9 +-
.../solr/client/solrj/impl/NoOpResponseParser.java | 6 +-
.../client/solrj/impl/SolrHttpClientBuilder.java | 5 -
.../solr/client/solrj/request/ApiMapping.java | 77 +++
.../solrj/request/CollectionAdminRequest.java | 15 +-
.../client/solrj/request/CollectionApiMapping.java | 265 ---------
.../solrj/request/ConfigSetAdminRequest.java | 4 +-
.../solrj/request/ContentStreamUpdateRequest.java | 3 +-
.../solr/client/solrj/request/CoreApiMapping.java | 6 +-
.../solrj/request/MultiContentWriterRequest.java | 2 +-
.../solrj/request/StreamingUpdateRequest.java | 4 +-
.../solr/client/solrj/request/V1toV2ApiMapper.java | 145 -----
.../solr/client/solrj/request/V2Request.java | 3 +-
...eReplicaPayload.java => AddReplicaPayload.java} | 47 +-
.../solrj/request/beans/CreateAliasPayload.java | 2 +-
...ReplicaPayload.java => CreateShardPayload.java} | 30 +-
...lectionPayload.java => ForceLeaderPayload.java} | 10 +-
...eReplicaPayload.java => SplitShardPayload.java} | 37 +-
...ollectionPayload.java => SyncShardPayload.java} | 10 +-
.../apache/solr/common/EmptyEntityResolver.java | 8 +-
.../apache/solr/common/cloud/LiveNodesWatcher.java | 26 -
.../apache/solr/common/cloud/PerReplicaStates.java | 4 +
.../solr/common/cloud/PerReplicaStatesOps.java | 20 +-
.../org/apache/solr/common/cloud/SolrZkClient.java | 8 +-
.../solr/common/params/CommonAdminParams.java | 2 +
.../apache/solr/common/util/JsonSchemaCreator.java | 2 +
.../solr/common/util/JsonSchemaValidator.java | 13 +
.../solr/common/util/LinkedSimpleHashMap.java | 40 --
solr/solrj/src/java/org/noggit/CharArr.java | 67 ---
.../collections.collection.Commands.modify.json | 16 -
.../collections.collection.shards.Commands.json | 129 -----
...llections.collection.shards.shard.Commands.json | 24 -
...collections.collection.shards.shard.delete.json | 27 -
...ons.collection.shards.shard.replica.delete.json | 39 --
.../solrj/solr/collection1/conf/schema-sql.xml | 28 -
.../solrj/solr/collection1/conf/schema.xml | 28 -
.../solrj/solr/collection1/conf/solrconfig-sql.xml | 6 -
.../solrj/solr/collection1/conf/solrconfig.xml | 10 +-
.../solrj/solr/configsets/ml/conf/solrconfig.xml | 6 -
.../solr/configsets/shared/conf/solrconfig.xml | 10 +-
.../solr/configsets/streaming/conf/schema.xml | 31 +-
.../solr/configsets/streaming/conf/solrconfig.xml | 6 -
.../solrj/solr/multicore/core0/conf/solrconfig.xml | 20 +-
.../solrj/solr/multicore/core1/conf/solrconfig.xml | 20 +-
.../client/solrj/io/stream/JDBCStreamTest.java | 2 +
.../solrj/io/stream/StreamExpressionTest.java | 2 +
.../client/solrj/request/TestV1toV2ApiMapper.java | 41 --
.../solr/client/solrj/request/TestV2Request.java | 2 +
.../cloud/PerReplicaStatesIntegrationTest.java | 47 ++
.../apache/solr/common/util/ContentStreamTest.java | 12 +-
.../apache/solr/common/util/JsonValidatorTest.java | 23 +-
.../solr/common/util/TestFastJavabinDecoder.java | 3 +-
.../apache/solr/EmbeddedSolrServerTestBase.java | 32 +-
.../org/apache/solr/SolrIgnoredThreadsFilter.java | 33 +-
.../java/org/apache/solr/SolrJettyTestBase.java | 45 +-
.../src/java/org/apache/solr/SolrTestCase.java | 2 +-
.../src/java/org/apache/solr/SolrTestCaseHS.java | 12 +-
.../src/java/org/apache/solr/SolrTestCaseJ4.java | 72 ++-
.../collections/AbstractIncrementalBackupTest.java | 5 +-
.../solr/core/MockConcurrentMergeScheduler.java | 34 --
.../org/apache/solr/util/BadHdfsThreadsFilter.java | 2 +
.../solr/util/BadMrClusterThreadsFilter.java | 37 --
.../java/org/apache/solr/util/DirectoryUtil.java | 57 ++
.../org/apache/solr/util/Log4jListAppender.java | 79 +++
.../src/java/org/apache/solr/util/TestHarness.java | 12 +-
.../solr/cloud/MiniSolrCloudClusterTest.java | 2 +
.../org/apache/solr/util/TestErrorLogMuter.java | 53 +-
solr/webapp/web/js/angular/controllers/files.js | 6 +-
.../web/js/angular/controllers/schema-designer.js | 9 +
solr/webapp/web/js/angular/controllers/security.js | 7 +
solr/webapp/web/partials/schema-designer.html | 20 +-
solr/webapp/web/partials/security.html | 6 +-
244 files changed, 2261 insertions(+), 3974 deletions(-)
delete mode 100644 solr/core/src/java/org/apache/solr/handler/ContentStreamLoader.java
delete mode 100644 solr/core/src/java/org/apache/solr/handler/admin/CollectionHandlerApi.java
copy solr/core/src/java/org/apache/solr/handler/admin/api/{AddReplicaPropertyAPI.java => AddReplicaAPI.java} (60%)
copy solr/core/src/java/org/apache/solr/handler/admin/api/{MigrateDocsAPI.java => CreateShardAPI.java} (58%)
copy solr/core/src/java/org/apache/solr/handler/admin/api/{CollectionStatusAPI.java => DeleteReplicaAPI.java} (56%)
copy solr/core/src/java/org/apache/solr/handler/admin/api/{CollectionStatusAPI.java => DeleteShardAPI.java} (52%)
create mode 100644 solr/core/src/java/org/apache/solr/handler/admin/api/ForceLeaderAPI.java
copy solr/core/src/java/org/apache/solr/handler/admin/api/{MigrateDocsAPI.java => SplitShardAPI.java} (57%)
create mode 100644 solr/core/src/java/org/apache/solr/handler/admin/api/SyncShardAPI.java
rename solr/core/src/{test/org/apache/solr/schema/ThrowErrorOnInitFieldType.java => java/org/apache/solr/handler/api/V2ApiUtils.java} (57%)
delete mode 100644 solr/core/src/java/org/apache/solr/legacy/DistanceValueSource.java
delete mode 100644 solr/core/src/java/org/apache/solr/legacy/PointVectorStrategy.java
create mode 100644 solr/core/src/java/org/apache/solr/logging/MDCSnapshot.java
delete mode 100644 solr/core/src/java/org/apache/solr/schema/GeoHashField.java
delete mode 100644 solr/core/src/java/org/apache/solr/schema/LatLonType.java
delete mode 100644 solr/core/src/java/org/apache/solr/schema/SpatialPointVectorFieldType.java
delete mode 100644 solr/core/src/java/org/apache/solr/schema/SpatialTermQueryPrefixTreeFieldType.java
delete mode 100644 solr/core/src/java/org/apache/solr/search/stats/CachedSearcherStats.java
delete mode 100644 solr/core/src/test/org/apache/solr/core/BlobStoreTestRequestHandler.java
delete mode 100644 solr/core/src/test/org/apache/solr/core/MockInfoBean.java
create mode 100644 solr/core/src/test/org/apache/solr/handler/TestRequestId.java
create mode 100644 solr/core/src/test/org/apache/solr/handler/admin/api/V2ShardsAPIMappingTest.java
create mode 100644 solr/core/src/test/org/apache/solr/request/TestSolrRequestInfo.java
create mode 100644 solr/solrj/src/java/org/apache/solr/client/solrj/request/ApiMapping.java
delete mode 100644 solr/solrj/src/java/org/apache/solr/client/solrj/request/CollectionApiMapping.java
delete mode 100644 solr/solrj/src/java/org/apache/solr/client/solrj/request/V1toV2ApiMapper.java
copy solr/solrj/src/java/org/apache/solr/client/solrj/request/beans/{MoveReplicaPayload.java => AddReplicaPayload.java} (55%)
copy solr/solrj/src/java/org/apache/solr/client/solrj/request/beans/{MoveReplicaPayload.java => CreateShardPayload.java} (71%)
copy solr/solrj/src/java/org/apache/solr/client/solrj/request/beans/{ReloadCollectionPayload.java => ForceLeaderPayload.java} (84%)
copy solr/solrj/src/java/org/apache/solr/client/solrj/request/beans/{MoveReplicaPayload.java => SplitShardPayload.java} (63%)
copy solr/solrj/src/java/org/apache/solr/client/solrj/request/beans/{ReloadCollectionPayload.java => SyncShardPayload.java} (84%)
delete mode 100644 solr/solrj/src/java/org/apache/solr/common/cloud/LiveNodesWatcher.java
delete mode 100644 solr/solrj/src/java/org/apache/solr/common/util/LinkedSimpleHashMap.java
delete mode 100644 solr/solrj/src/resources/apispec/collections.collection.Commands.modify.json
delete mode 100644 solr/solrj/src/resources/apispec/collections.collection.shards.Commands.json
delete mode 100644 solr/solrj/src/resources/apispec/collections.collection.shards.shard.Commands.json
delete mode 100644 solr/solrj/src/resources/apispec/collections.collection.shards.shard.delete.json
delete mode 100644 solr/solrj/src/resources/apispec/collections.collection.shards.shard.replica.delete.json
delete mode 100644 solr/solrj/src/test/org/apache/solr/client/solrj/request/TestV1toV2ApiMapper.java
delete mode 100644 solr/test-framework/src/java/org/apache/solr/core/MockConcurrentMergeScheduler.java
delete mode 100644 solr/test-framework/src/java/org/apache/solr/util/BadMrClusterThreadsFilter.java
create mode 100644 solr/test-framework/src/java/org/apache/solr/util/DirectoryUtil.java
create mode 100644 solr/test-framework/src/java/org/apache/solr/util/Log4jListAppender.java
[solr] 27/28: addressed review comments
Posted by is...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
ishan pushed a commit to branch jira/SOLR15694
in repository https://gitbox.apache.org/repos/asf/solr.git
commit 984edbf8959a08392651c49aed302ffdec5338b3
Author: Noble Paul <no...@gmail.com>
AuthorDate: Wed Dec 22 23:19:52 2021 +1100
addressed review comments
---
.../apache/solr/cloud/api/collections/Assign.java | 30 ++++++++--------------
.../src/java/org/apache/solr/cluster/Cluster.java | 1 +
.../placement/impl/PlacementRequestImpl.java | 2 +-
.../impl/SimpleClusterAbstractionsImpl.java | 15 ++++++++++-
.../src/java/org/apache/solr/core/NodeRoles.java | 17 +++++-------
.../placement/ClusterAbstractionsForTest.java | 5 ++++
6 files changed, 39 insertions(+), 31 deletions(-)
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/Assign.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/Assign.java
index 7dfcc33..6111b3c 100644
--- a/solr/core/src/java/org/apache/solr/cloud/api/collections/Assign.java
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/Assign.java
@@ -21,18 +21,7 @@ import static org.apache.solr.common.cloud.ZkStateReader.CORE_NAME_PROP;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Random;
-import java.util.Set;
+import java.util.*;
import java.util.stream.Collectors;
import org.apache.solr.client.solrj.cloud.DistribStateManager;
@@ -246,22 +235,25 @@ public class Assign {
Collections.shuffle(nodeList, random);
}
} else {
- nodeList = new ArrayList<>(liveNodes);
- filterNonDataNodes(zk, nodeList);
+ nodeList = new ArrayList<>(filterNonDataNodes(zk, liveNodes));
Collections.shuffle(nodeList, random);
}
return nodeList;
}
- public static void filterNonDataNodes(DistribStateManager zk, List<String> liveNodes) {
+ public static Collection<String> filterNonDataNodes(DistribStateManager zk, Collection<String> liveNodes) {
try {
List<String> noData = ClusterAPI.getNodesByRole(NodeRoles.Role.DATA, NodeRoles.Mode.OFF, zk);
- if (!noData.isEmpty()) {
- liveNodes.removeAll(noData);
- }
+ if (noData.isEmpty()) {
+ return liveNodes;
+ } else {
+ liveNodes = new HashSet<>(liveNodes);
+ liveNodes.removeAll(noData);
+ return liveNodes;
+ }
} catch (Exception e) {
- throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Problem fetching roles from Zookeeper", e);
+ throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Error fetching roles from Zookeeper", e);
}
}
diff --git a/solr/core/src/java/org/apache/solr/cluster/Cluster.java b/solr/core/src/java/org/apache/solr/cluster/Cluster.java
index 3b7bdd4..4cd32a4 100644
--- a/solr/core/src/java/org/apache/solr/cluster/Cluster.java
+++ b/solr/core/src/java/org/apache/solr/cluster/Cluster.java
@@ -31,6 +31,7 @@ public interface Cluster {
*/
Set<Node> getLiveNodes();
+ Set<Node> getLiveNodes(boolean filterNonDataNodes) ;
/**
* Returns info about the given collection if one exists.
*
diff --git a/solr/core/src/java/org/apache/solr/cluster/placement/impl/PlacementRequestImpl.java b/solr/core/src/java/org/apache/solr/cluster/placement/impl/PlacementRequestImpl.java
index ff3f090..3814eb6 100644
--- a/solr/core/src/java/org/apache/solr/cluster/placement/impl/PlacementRequestImpl.java
+++ b/solr/core/src/java/org/apache/solr/cluster/placement/impl/PlacementRequestImpl.java
@@ -86,7 +86,7 @@ public class PlacementRequestImpl implements PlacementRequest {
throw new Assign.AssignmentException("Bad assign request: empty list of nodes for collection " + solrCollection.getName());
}
} else {
- nodes = cluster.getLiveNodes();
+ nodes = cluster.getLiveNodes(true);
if (nodes.isEmpty()) {
throw new Assign.AssignmentException("Impossible assign request: no live nodes for collection " + solrCollection.getName());
}
diff --git a/solr/core/src/java/org/apache/solr/cluster/placement/impl/SimpleClusterAbstractionsImpl.java b/solr/core/src/java/org/apache/solr/cluster/placement/impl/SimpleClusterAbstractionsImpl.java
index 48f3f50..64c0b64 100644
--- a/solr/core/src/java/org/apache/solr/cluster/placement/impl/SimpleClusterAbstractionsImpl.java
+++ b/solr/core/src/java/org/apache/solr/cluster/placement/impl/SimpleClusterAbstractionsImpl.java
@@ -23,6 +23,7 @@ import java.util.stream.Collectors;
import com.google.common.collect.Maps;
import org.apache.solr.client.solrj.cloud.SolrCloudManager;
+import org.apache.solr.cloud.api.collections.Assign;
import org.apache.solr.cluster.*;
import org.apache.solr.common.cloud.ClusterState;
import org.apache.solr.common.cloud.DocCollection;
@@ -53,10 +54,16 @@ class SimpleClusterAbstractionsImpl {
static class ClusterImpl implements Cluster {
private final Set<Node> liveNodes;
+ private final Set<Node> liveNodesWithData;
private final ClusterState clusterState;
ClusterImpl(SolrCloudManager solrCloudManager) throws IOException {
- liveNodes = NodeImpl.getNodes(solrCloudManager.getClusterStateProvider().getLiveNodes());
+ Set<String> liveNodes = solrCloudManager.getClusterStateProvider().getLiveNodes();
+ Collection<String> liveNodesWithData = Assign.filterNonDataNodes( solrCloudManager.getDistribStateManager(), liveNodes);
+ this.liveNodes = NodeImpl.getNodes(liveNodes);
+ this.liveNodesWithData = liveNodesWithData.size() == liveNodes.size() ?
+ this.liveNodes :
+ NodeImpl.getNodes(liveNodesWithData);
clusterState = solrCloudManager.getClusterStateProvider().getClusterState();
}
@@ -65,6 +72,12 @@ class SimpleClusterAbstractionsImpl {
return liveNodes;
}
+ public Set<Node> getLiveNodes(boolean filterNonDataNodes) {
+ return filterNonDataNodes ?
+ liveNodesWithData :
+ liveNodes;
+ }
+
@Override
public SolrCollection getCollection(String collectionName) {
return SolrCollectionImpl.createCollectionFacade(clusterState, collectionName);
diff --git a/solr/core/src/java/org/apache/solr/core/NodeRoles.java b/solr/core/src/java/org/apache/solr/core/NodeRoles.java
index 714d8b1..317f771 100644
--- a/solr/core/src/java/org/apache/solr/core/NodeRoles.java
+++ b/solr/core/src/java/org/apache/solr/core/NodeRoles.java
@@ -18,7 +18,6 @@ package org.apache.solr.core;
import java.lang.invoke.MethodHandles;
import java.util.*;
-import org.apache.solr.common.MapWriter;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.StringUtils;
import org.apache.solr.common.cloud.ZkStateReader;
@@ -61,7 +60,7 @@ public class NodeRoles {
}
for(Role r: Role.values()) {
if (!roles.containsKey(r)) {
- roles.put(r, r.defaultIfAbsent());
+ roles.put(r, r.defaultMode());
}
}
nodeRoles = Collections.unmodifiableMap(roles);
@@ -100,7 +99,7 @@ public class NodeRoles {
return Set.of(Mode.ON, Mode.OFF);
}
@Override
- public Mode defaultIfAbsent() {
+ public Mode defaultMode() {
return Mode.OFF;
}
},
@@ -110,7 +109,7 @@ public class NodeRoles {
return Set.of(Mode.ALLOWED, Mode.PREFERRED, Mode.DISALLOWED);
}
@Override
- public Mode defaultIfAbsent() {
+ public Mode defaultMode() {
return Mode.DISALLOWED;
}
};
@@ -122,12 +121,10 @@ public class NodeRoles {
}
public static Role getRole(String value) {
- try {
- Role role = Role.valueOf(value.toUpperCase(Locale.ROOT));
- return role;
- } catch (IllegalArgumentException ex) {
- throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown role: " + value);
+ for (Role role: Role.values()) {
+ if (value.equals(role.roleName)) return role;
}
+ throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown role: " + value);
}
public abstract Set<Mode> supportedModes();
@@ -135,7 +132,7 @@ public class NodeRoles {
/**
* Default mode for a role in nodes where this role is not specified.
*/
- public abstract Mode defaultIfAbsent();
+ public abstract Mode defaultMode();
@Override
public String toString() {
diff --git a/solr/core/src/test/org/apache/solr/cluster/placement/ClusterAbstractionsForTest.java b/solr/core/src/test/org/apache/solr/cluster/placement/ClusterAbstractionsForTest.java
index 1260a10..bb85bd3 100644
--- a/solr/core/src/test/org/apache/solr/cluster/placement/ClusterAbstractionsForTest.java
+++ b/solr/core/src/test/org/apache/solr/cluster/placement/ClusterAbstractionsForTest.java
@@ -43,6 +43,11 @@ class ClusterAbstractionsForTest {
}
@Override
+ public Set<Node> getLiveNodes(boolean filterNonDataNodes) {
+ return liveNodes;
+ }
+
+ @Override
public SolrCollection getCollection(String collectionName) {
return collections.get(collectionName);
}
[solr] 14/28: supportedroles and overseer:disallowed supported
Posted by is...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
ishan pushed a commit to branch jira/SOLR15694
in repository https://gitbox.apache.org/repos/asf/solr.git
commit ff26ae58f413155470c85e6a87d31d6d1dddad0a
Author: Noble Paul <no...@gmail.com>
AuthorDate: Sat Dec 11 11:20:54 2021 +1100
supportedroles and overseer:disallowed supported
---
.../java/org/apache/solr/cloud/ZkController.java | 14 +++++++----
.../src/java/org/apache/solr/core/NodeRoles.java | 8 ++++++-
.../java/org/apache/solr/handler/ClusterAPI.java | 27 +++++++++++-----------
.../test/org/apache/solr/cloud/NodeRolesTest.java | 8 ++++++-
4 files changed, 37 insertions(+), 20 deletions(-)
diff --git a/solr/core/src/java/org/apache/solr/cloud/ZkController.java b/solr/core/src/java/org/apache/solr/cloud/ZkController.java
index 5e71098..33ea43d 100644
--- a/solr/core/src/java/org/apache/solr/cloud/ZkController.java
+++ b/solr/core/src/java/org/apache/solr/cloud/ZkController.java
@@ -352,7 +352,9 @@ public class ZkController implements Closeable {
overseerElector.setup(context);
- overseerElector.joinElection(context, true);
+ if(cc.nodeRoles.isOverseerAllowed()) {
+ overseerElector.joinElection(context, true);
+ }
}
cc.cancelCoreRecoveries();
@@ -683,7 +685,9 @@ public class ZkController implements Closeable {
} finally {
// just in case the OverseerElectionContext managed to start another Overseer
- IOUtils.closeQuietly(overseer);
+ if(overseer != null) {
+ IOUtils.closeQuietly(overseer);
+ }
ExecutorUtil.shutdownAndAwaitTermination(customThreadPool);
}
@@ -909,14 +913,16 @@ public class ZkController implements Closeable {
registerLiveNodesListener();
// start the overseer first as following code may need it's processing
- if (!zkRunOnly) {
+ if (!zkRunOnly && cc.nodeRoles.isOverseerAllowed()) {
overseerElector = new LeaderElector(zkClient);
this.overseer = new Overseer((HttpShardHandler) cc.getShardHandlerFactory().getShardHandler(), cc.getUpdateShardHandler(),
CommonParams.CORES_HANDLER_PATH, zkStateReader, this, cloudConfig);
ElectionContext context = new OverseerElectionContext(zkClient,
overseer, getNodeName());
overseerElector.setup(context);
- overseerElector.joinElection(context, false);
+ if(cc.nodeRoles.isOverseerAllowed()) {
+ overseerElector.joinElection(context, false);
+ }
}
Stat stat = zkClient.exists(ZkStateReader.LIVE_NODES_ZKNODE, null, true);
diff --git a/solr/core/src/java/org/apache/solr/core/NodeRoles.java b/solr/core/src/java/org/apache/solr/core/NodeRoles.java
index b72e5ec..4bb04e0 100644
--- a/solr/core/src/java/org/apache/solr/core/NodeRoles.java
+++ b/solr/core/src/java/org/apache/solr/core/NodeRoles.java
@@ -35,6 +35,7 @@ public class NodeRoles implements MapWriter {
public static final String ALLOWED = "allowed";
public static final String DISALLOWED = "disallowed";
public static final String PREFERRED = "preferred";
+ public static final Set<String> OVERSEER_MODES = Set.of(ALLOWED, PREFERRED, DISALLOWED);
public static final String DEFAULT_ROLES_STRING = "data:on,overseer:allowed";
@@ -77,12 +78,17 @@ public class NodeRoles implements MapWriter {
nodeRoles.forEach((role, s) -> ew.putNoEx(role.roleName, s));
}
+ public boolean isOverseerAllowed() {
+ String roleMode = nodeRoles.get(Role.OVERSEER);
+ return ALLOWED.equals(roleMode) || PREFERRED.equals(roleMode);
+ }
+
public enum Role {
DATA("data"),
OVERSEER("overseer") {
@Override
public Set<String> supportedModes() {
- return Set.of(ALLOWED, PREFERRED, DISALLOWED);
+ return OVERSEER_MODES;
}
@Override
public String defaultIfAbsent() {
diff --git a/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java b/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
index 796ac49..446cd9b 100644
--- a/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
+++ b/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
@@ -84,41 +84,32 @@ public class ClusterAPI {
@EndPoint(method = GET,
path = "/cluster/node-roles",
permission = COLL_READ_PERM)
- @SuppressWarnings("unchecked")
// nocommit: it must also output all data nodes that didn't start with -Dsolr.node.roles
public void roles(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
Map<String, Object> result = new LinkedHashMap<>();
- rsp.add("node-roles", readRecursive(ZkStateReader.NODE_ROLES, collectionsHandler.getCoreContainer().getZkController().getSolrCloudManager().getDistribStateManager(), 3));
+ rsp.add("node-roles", readRecursive(ZkStateReader.NODE_ROLES,
+ collectionsHandler.getCoreContainer().getZkController().getSolrCloudManager().getDistribStateManager(), 3));
}
Object readRecursive(String path, DistribStateManager zk, int depth) {
if (depth == 0) return null;
Map<String, Object> result = null;
- boolean hasSubValues = false;
try {
List<String> children = zk.listData(path);
if (children != null && !children.isEmpty()) {
result = new HashMap<>();
} else {
- return depth >=1 ? Collections.emptyList(): null;
+ return depth >= 1 ? Collections.emptyList() : null;
}
for (String child : children) {
Object c = readRecursive(path + "/" + child, zk, depth - 1);
- if (c != null) {
- hasSubValues = true;
- }
+
result.put(child, c);
}
-
} catch (Exception e) {
- }
-
-
-
- if (result == null) {
- return null;
+ throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e);
}
if (depth == 1) {
return result.keySet();
@@ -138,6 +129,14 @@ public class ClusterAPI {
collectionsHandler.getCoreContainer().getZkController().getSolrCloudManager().getDistribStateManager(), 2));
}
@EndPoint(method = GET,
+ path = "/cluster/node-roles/supported",
+ permission = COLL_READ_PERM)
+ public void supportedRoles(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
+ rsp.add("supported-roles",
+ collectionsHandler.getCoreContainer().getZkController().getSolrCloudManager().getDistribStateManager().listData(ZkStateReader.NODE_ROLES));
+ }
+
+ @EndPoint(method = GET,
path = "/cluster/node-roles/{role}/{role-val}",
permission = COLL_READ_PERM)
public void nodesWithRoleVal(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
diff --git a/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java b/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java
index a2e3651..cda83ad 100644
--- a/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java
@@ -21,6 +21,7 @@ import java.lang.invoke.MethodHandles;
import java.util.Collection;
import java.util.Collections;
+import java.util.List;
import org.apache.solr.client.solrj.embedded.JettySolrRunner;
import org.apache.solr.client.solrj.request.CollectionAdminRequest;
import org.apache.solr.client.solrj.request.V2Request;
@@ -50,9 +51,14 @@ public class NodeRolesTest extends SolrCloudTestCase {
public void testRoleIntegration() throws Exception {
JettySolrRunner j0 = cluster.getJettySolrRunner(0);
JettySolrRunner j1 = null, j2 = null;
+ V2Response rsp = new V2Request.Builder("/cluster/node-roles/supported").GET().build().process(cluster.getSolrClient());
+ List<String> l = (List<String>) rsp._get("supported-roles", Collections.emptyList());
+ assertTrue(l.contains("data"));
+ assertTrue(l.contains("overseer"));
+
j1 = startNodeWithRoles("overseer:preferred,data:off");
- V2Response rsp = new V2Request.Builder("/cluster/node-roles").GET().build().process(cluster.getSolrClient());
+ rsp = new V2Request.Builder("/cluster/node-roles").GET().build().process(cluster.getSolrClient());
assertEquals(j1.getNodeName(), rsp._getStr("node-roles/overseer/preferred[0]", null));
assertEquals(j1.getNodeName(), rsp._getStr("node-roles/data/off[0]", null));
OverseerRolesTest.waitForNewOverseer(20, j1.getNodeName(), false);
[solr] 01/28: Node role impl
Posted by is...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
ishan pushed a commit to branch jira/SOLR15694
in repository https://gitbox.apache.org/repos/asf/solr.git
commit fa036abc916bf92aceafcdb759fc2440c725191e
Author: Noble Paul <no...@gmail.com>
AuthorDate: Wed Nov 10 00:10:09 2021 +1100
Node role impl
---
.../java/org/apache/solr/cloud/ZkController.java | 38 +++++--
.../impl/SimpleClusterAbstractionsImpl.java | 27 ++++-
.../java/org/apache/solr/core/CoreContainer.java | 2 +
.../src/java/org/apache/solr/core/NodeRole.java | 122 +++++++++++++++++++++
.../test/org/apache/solr/core/TestNodeRole.java | 53 +++++++++
5 files changed, 234 insertions(+), 8 deletions(-)
diff --git a/solr/core/src/java/org/apache/solr/cloud/ZkController.java b/solr/core/src/java/org/apache/solr/cloud/ZkController.java
index 75a4e83..8dee09e 100644
--- a/solr/core/src/java/org/apache/solr/cloud/ZkController.java
+++ b/solr/core/src/java/org/apache/solr/cloud/ZkController.java
@@ -46,6 +46,9 @@ import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.Function;
import java.util.function.Supplier;
import com.google.common.base.Strings;
@@ -66,6 +69,7 @@ import org.apache.solr.common.cloud.*;
import org.apache.solr.common.cloud.Replica.Type;
import org.apache.solr.common.params.CollectionParams;
import org.apache.solr.common.params.CommonParams;
+import org.apache.solr.common.params.CoreAdminParams;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.util.ExecutorUtil;
import org.apache.solr.common.util.IOUtils;
@@ -74,12 +78,7 @@ import org.apache.solr.common.util.SolrNamedThreadFactory;
import org.apache.solr.common.util.StrUtils;
import org.apache.solr.common.util.URLUtil;
import org.apache.solr.common.util.Utils;
-import org.apache.solr.core.CloseHook;
-import org.apache.solr.core.CloudConfig;
-import org.apache.solr.core.CoreContainer;
-import org.apache.solr.core.CoreDescriptor;
-import org.apache.solr.core.SolrCore;
-import org.apache.solr.core.SolrCoreInitializationException;
+import org.apache.solr.core.*;
import org.apache.solr.handler.component.HttpShardHandler;
import org.apache.solr.logging.MDCLoggingContext;
import org.apache.solr.search.SolrIndexSearcher;
@@ -105,6 +104,8 @@ import static org.apache.solr.common.cloud.ZkStateReader.ELECTION_NODE_PROP;
import static org.apache.solr.common.cloud.ZkStateReader.NODE_NAME_PROP;
import static org.apache.solr.common.cloud.ZkStateReader.REJOIN_AT_HEAD_PROP;
import static org.apache.solr.common.cloud.ZkStateReader.SHARD_ID_PROP;
+import static org.apache.solr.common.params.CollectionParams.CollectionAction.ADDROLE;
+import static org.apache.solr.common.params.CoreAdminParams.ACTION;
import static org.apache.zookeeper.ZooDefs.Ids.OPEN_ACL_UNSAFE;
/**
@@ -465,6 +466,7 @@ public class ZkController implements Closeable {
});
init();
+ persistRoles();
if (distributedClusterStateUpdater.isDistributedStateUpdate()) {
this.overseerJobQueue = null;
@@ -479,6 +481,28 @@ public class ZkController implements Closeable {
assert ObjectReleaseTracker.track(this);
}
+ @SuppressWarnings("unchecked")
+ private void persistRoles() throws InterruptedException {
+ try {
+ zkClient.atomicUpdate(ZkStateReader.ROLES,
+ bytes -> {
+ Map<String, Object> map = bytes == null ? null :
+ (Map<String, Object>) Utils.fromJSON(bytes);
+ map = cc.nodeRole.modifyRoleData(map,
+ getNodeName());
+ return map == null ? null : Utils.toJSON(map);
+ });
+ if (cc.nodeRole.role() == NodeRole.Type.overseer) {
+ getOverseerCollectionQueue()
+ .offer(Utils.toJSON(Utils.makeMap(ACTION, ADDROLE.name(),
+ "role", NodeRole.Type.overseer,
+ "node", getNodeName())));
+ }
+ } catch (KeeperException e) {
+ throw new SolrException(ErrorCode.SERVER_ERROR, "can't update roles");
+ }
+ }
+
/**
* <p>Verifies if /clusterstate.json exists in Zookeepeer, and if it does and is not empty, refuses to start and outputs
* a helpful message regarding collection migration.</p>
@@ -2198,7 +2222,7 @@ public class ZkController implements Closeable {
List<?> nodeList = (List<?>) roles.get("overseer");
if (nodeList == null) return;
if (nodeList.contains(getNodeName())) {
- ZkNodeProps props = new ZkNodeProps(Overseer.QUEUE_OPERATION, CollectionParams.CollectionAction.ADDROLE.toString().toLowerCase(Locale.ROOT),
+ ZkNodeProps props = new ZkNodeProps(Overseer.QUEUE_OPERATION, ADDROLE.toString().toLowerCase(Locale.ROOT),
"node", getNodeName(),
"role", "overseer");
log.info("Going to add role {} ", props);
diff --git a/solr/core/src/java/org/apache/solr/cluster/placement/impl/SimpleClusterAbstractionsImpl.java b/solr/core/src/java/org/apache/solr/cluster/placement/impl/SimpleClusterAbstractionsImpl.java
index 48f3f50..1d5bec6 100644
--- a/solr/core/src/java/org/apache/solr/cluster/placement/impl/SimpleClusterAbstractionsImpl.java
+++ b/solr/core/src/java/org/apache/solr/cluster/placement/impl/SimpleClusterAbstractionsImpl.java
@@ -23,12 +23,18 @@ import java.util.stream.Collectors;
import com.google.common.collect.Maps;
import org.apache.solr.client.solrj.cloud.SolrCloudManager;
+import org.apache.solr.client.solrj.cloud.VersionedData;
import org.apache.solr.cluster.*;
+import org.apache.solr.common.SolrException;
import org.apache.solr.common.cloud.ClusterState;
import org.apache.solr.common.cloud.DocCollection;
import org.apache.solr.common.cloud.Slice;
+import org.apache.solr.common.cloud.ZkStateReader;
import org.apache.solr.common.params.CollectionAdminParams;
import org.apache.solr.common.util.Pair;
+import org.apache.solr.common.util.Utils;
+import org.apache.solr.core.NodeRole;
+import org.apache.zookeeper.KeeperException;
import javax.annotation.Nonnull;
@@ -56,7 +62,26 @@ class SimpleClusterAbstractionsImpl {
private final ClusterState clusterState;
ClusterImpl(SolrCloudManager solrCloudManager) throws IOException {
- liveNodes = NodeImpl.getNodes(solrCloudManager.getClusterStateProvider().getLiveNodes());
+ Set<String> liveNodes = solrCloudManager.getClusterStateProvider().getLiveNodes();
+ try {
+ if (solrCloudManager.getDistribStateManager().hasData(ZkStateReader.ROLES)) {
+ VersionedData rolesData = solrCloudManager.getDistribStateManager().getData(ZkStateReader.ROLES);
+ if (rolesData != null && rolesData.getData().length > 0) {
+ Map<String, Object> roles = (Map<String, Object>) Utils.fromJSON(rolesData.getData());
+ List<String> noReplicasNodes = (List<String>) roles.get(NodeRole.NO_REPLICAS);
+ if (noReplicasNodes != null && !noReplicasNodes.isEmpty()) {
+ liveNodes = new HashSet<>(liveNodes);
+ liveNodes.removeAll(noReplicasNodes);
+ }
+ }
+ }
+
+ } catch (NoSuchElementException nsee) {
+ //no problem
+ } catch (Exception e) {
+ throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unable to communicate with Zookeeper");
+ }
+ this.liveNodes = NodeImpl.getNodes(liveNodes);
clusterState = solrCloudManager.getClusterStateProvider().getClusterState();
}
diff --git a/solr/core/src/java/org/apache/solr/core/CoreContainer.java b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
index d4d2030..f2c2b78 100644
--- a/solr/core/src/java/org/apache/solr/core/CoreContainer.java
+++ b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
@@ -246,6 +246,8 @@ public class CoreContainer {
private final ObjectCache objectCache = new ObjectCache();
+ public final NodeRole nodeRole = new NodeRole(System.getProperty("node.roles"));
+
private final ClusterSingletons clusterSingletons = new ClusterSingletons(
() -> getZkController() != null &&
getZkController().getOverseer() != null &&
diff --git a/solr/core/src/java/org/apache/solr/core/NodeRole.java b/solr/core/src/java/org/apache/solr/core/NodeRole.java
new file mode 100644
index 0000000..1de7832
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/core/NodeRole.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.
+ */
+package org.apache.solr.core;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.*;
+import org.apache.solr.common.StringUtils;
+import org.apache.solr.common.util.StrUtils;
+
+public class NodeRole {
+ private boolean hasData;
+ private Type role;
+
+ public NodeRole(String role) {
+ if (StringUtils.isEmpty(role)) {
+ hasData = true;
+ this.role = Type.data;
+ return;
+ }
+ Set<String> roles = new HashSet<>(StrUtils.split(role, ','));
+ if (roles.isEmpty()) {
+ hasData = true;
+ return;
+ }
+ for (String s : roles) {
+ if (Type.data.name().equals(s)) {
+ hasData = true;
+ continue;
+ }
+ if (Type.overseer.name().equals(s)) {
+ this.role = Type.overseer;
+ } else {
+ throw new RuntimeException("Unknown type");
+ }
+ }
+
+ }
+
+ public boolean hasData() {
+ return hasData;
+ }
+
+ public Type role() {
+ return role;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Map<String, Object> modifyRoleData(Map<String, Object> rolesData, String nodeName) {
+ if (this.role == Type.data) {
+ //this is a normal data node, nothing to do
+ if (rolesData == null) return null;// no role info, return
+ if (removeStaleRoles(nodeName, rolesData, Collections.emptySet())) {
+ return rolesData;
+ } else {
+ return null;
+ }
+ } else {
+ if (rolesData == null) rolesData = new HashMap<>();
+ List<String> nodes = (List<String>) rolesData.computeIfAbsent(role.name(), s -> new ArrayList<String>());
+ boolean isModified = false;
+ if (!nodes.contains(nodeName)) {
+ nodes.add(nodeName);
+ isModified = true;
+ }
+ List<String> nonReplicaNodes = (List<String>) rolesData.computeIfAbsent(NO_REPLICAS, s -> new ArrayList<String>());
+ if (hasData) {
+ if(nonReplicaNodes.contains(nodeName)) {
+ nonReplicaNodes.remove(nodeName);
+ isModified = true;
+ }
+ } else {
+ if(!nonReplicaNodes.contains(nodeName)){
+ nonReplicaNodes.add(nodeName);
+ isModified = true;
+ }
+ }
+ if (isModified || removeStaleRoles(nodeName, rolesData, ImmutableSet.of(role.name(), NO_REPLICAS))) {
+ return rolesData;
+ } else {
+ return null;
+ }
+ }
+
+ }
+
+ @SuppressWarnings("unchecked")
+ private boolean removeStaleRoles(String nodeName, Map<String, Object> rolesData, Set<String> ignore) {
+ boolean[] isModified = new boolean[]{false};
+ rolesData.forEach((s, o) -> {
+ if (ignore.contains(s)) return;
+ if (o instanceof List) {
+ List<String> list = (List<String>) o;
+ if (list.contains(nodeName)) {
+ isModified[0] = true;
+ list.remove(nodeName);
+ }
+ }
+ });
+ return isModified[0];
+ }
+
+ public enum Type {
+ data, overseer;
+ }
+
+ public static final String NO_REPLICAS = "no-replicas";
+
+}
diff --git a/solr/core/src/test/org/apache/solr/core/TestNodeRole.java b/solr/core/src/test/org/apache/solr/core/TestNodeRole.java
new file mode 100644
index 0000000..d5bdba5
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/core/TestNodeRole.java
@@ -0,0 +1,53 @@
+package org.apache.solr.core;
+
+import java.lang.invoke.MethodHandles;
+import java.util.Collection;
+import java.util.Map;
+import org.apache.solr.SolrTestCase;
+import org.apache.solr.common.util.Utils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class TestNodeRole extends SolrTestCase {
+ private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+
+ @SuppressWarnings("rawtypes")
+ public void testZkData() {
+ //empty
+ Map<String,Object> rolesData = null;
+ //start a node with overseer role
+ rolesData = new NodeRole("overseer").modifyRoleData(rolesData, "node1");
+ assertEquals("node1", _val(rolesData, "overseer[0]"));
+ assertEquals("node1", _val(rolesData, "no-replicas[0]"));
+ //now start another node with overseer
+ rolesData = new NodeRole("overseer").modifyRoleData(rolesData, "node2");
+ assertEquals("node2", _val(rolesData, "overseer[1]"));
+ assertEquals("node2", _val(rolesData, "no-replicas[1]"));
+ //now start another node with overseer,data
+ rolesData = new NodeRole("overseer,data").modifyRoleData(rolesData, "node3");
+
+ assertEquals("node3", _val(rolesData, "overseer[2]"));
+ assertEquals(2, ((Collection) _val(rolesData, "no-replicas")).size() );
+ //now restart node2 with no role
+
+ rolesData = new NodeRole(null).modifyRoleData(rolesData, "node2");
+ assertEquals("node3", _val(rolesData, "overseer[1]"));
+ assertEquals(1, ((Collection) _val(rolesData, "no-replicas")).size() );
+ //now restart node1 with the original values
+ assertNull(new NodeRole("overseer").modifyRoleData(rolesData, "node1"));
+
+ assertEquals("node1", _val(rolesData, "overseer[0]"));
+ assertEquals("node1", _val(rolesData, "no-replicas[0]"));
+ //now restart node1 with overseer,data
+ rolesData = new NodeRole("overseer,data").modifyRoleData(rolesData, "node1");
+ assertEquals("node1", _val(rolesData, "overseer[0]"));
+ assertEquals(0, ((Collection) _val(rolesData, "no-replicas")).size() );
+
+ log.info("rolesdata : {}", Utils.toJSONString(rolesData));
+ }
+
+ private Object _val(Map<String, Object> rolesData, String path) {
+ return Utils.getObjectByPath(rolesData, false, path);
+ }
+}
[solr] 12/28: new format
Posted by is...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
ishan pushed a commit to branch jira/SOLR15694
in repository https://gitbox.apache.org/repos/asf/solr.git
commit 2716b06007ad7ff541a9c60b17965251a9420fa4
Author: Noble Paul <no...@gmail.com>
AuthorDate: Fri Dec 10 16:51:14 2021 +1100
new format
---
.../apache/solr/cloud/OverseerNodePrioritizer.java | 3 +-
.../java/org/apache/solr/cloud/ZkController.java | 18 ++-
.../apache/solr/cloud/api/collections/Assign.java | 17 +--
.../java/org/apache/solr/core/CoreContainer.java | 6 +-
.../src/java/org/apache/solr/core/NodeRole.java | 77 ----------
.../src/java/org/apache/solr/core/NodeRoles.java | 107 +++++++++++++
.../java/org/apache/solr/handler/ClusterAPI.java | 166 +++++++++++++--------
.../test/org/apache/solr/cloud/NodeRolesTest.java | 40 +++--
.../client/solrj/cloud/DistribStateManager.java | 23 ---
9 files changed, 258 insertions(+), 199 deletions(-)
diff --git a/solr/core/src/java/org/apache/solr/cloud/OverseerNodePrioritizer.java b/solr/core/src/java/org/apache/solr/cloud/OverseerNodePrioritizer.java
index f88c686..4bd21dd 100644
--- a/solr/core/src/java/org/apache/solr/cloud/OverseerNodePrioritizer.java
+++ b/solr/core/src/java/org/apache/solr/cloud/OverseerNodePrioritizer.java
@@ -28,6 +28,7 @@ import org.apache.solr.common.params.CoreAdminParams;
import org.apache.solr.common.params.CoreAdminParams.CoreAdminAction;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.util.Utils;
+import org.apache.solr.core.NodeRoles;
import org.apache.solr.handler.ClusterAPI;
import org.apache.solr.handler.component.ShardHandler;
import org.apache.solr.handler.component.ShardHandlerFactory;
@@ -72,7 +73,7 @@ public class OverseerNodePrioritizer {
}
}
- overseerDesignates.addAll(ClusterAPI.getNodesByRole("overseer", new ZkDistribStateManager(zkStateReader.getZkClient())));
+ overseerDesignates.addAll(ClusterAPI.getNodesByRole(NodeRoles.Role.OVERSEER, NodeRoles.PREFERRED, new ZkDistribStateManager(zkStateReader.getZkClient())));
if (overseerDesignates.isEmpty()) return;
String ldr = OverseerTaskProcessor.getLeaderNode(zk);
if(overseerDesignates.contains(ldr)) return;
diff --git a/solr/core/src/java/org/apache/solr/cloud/ZkController.java b/solr/core/src/java/org/apache/solr/cloud/ZkController.java
index 05c6c5b..e7b15bf 100644
--- a/solr/core/src/java/org/apache/solr/cloud/ZkController.java
+++ b/solr/core/src/java/org/apache/solr/cloud/ZkController.java
@@ -73,12 +73,7 @@ import org.apache.solr.common.util.SolrNamedThreadFactory;
import org.apache.solr.common.util.StrUtils;
import org.apache.solr.common.util.URLUtil;
import org.apache.solr.common.util.Utils;
-import org.apache.solr.core.CloseHook;
-import org.apache.solr.core.CloudConfig;
-import org.apache.solr.core.CoreContainer;
-import org.apache.solr.core.CoreDescriptor;
-import org.apache.solr.core.SolrCore;
-import org.apache.solr.core.SolrCoreInitializationException;
+import org.apache.solr.core.*;
import org.apache.solr.handler.component.HttpShardHandler;
import org.apache.solr.logging.MDCLoggingContext;
import org.apache.solr.search.SolrIndexSearcher;
@@ -855,6 +850,13 @@ public class ZkController implements Closeable {
ZkCmdExecutor cmdExecutor = new ZkCmdExecutor(zkClient.getZkClientTimeout());
cmdExecutor.ensureExists(ZkStateReader.LIVE_NODES_ZKNODE, zkClient);
cmdExecutor.ensureExists(ZkStateReader.NODE_ROLES, zkClient);
+ for (NodeRoles.Role role : NodeRoles.Role.values()) {
+ cmdExecutor.ensureExists(ZkStateReader.NODE_ROLES + "/" + role.roleName, zkClient);
+ for (String v : role.supportedVals()) {
+ cmdExecutor.ensureExists(ZkStateReader.NODE_ROLES + "/" + role.roleName + "/"+v, zkClient);
+ }
+ }
+
cmdExecutor.ensureExists(ZkStateReader.COLLECTIONS_ZKNODE, zkClient);
cmdExecutor.ensureExists(ZkStateReader.ALIASES, zkClient);
byte[] emptyJson = "{}".getBytes(StandardCharsets.UTF_8);
@@ -1089,8 +1091,8 @@ public class ZkController implements Closeable {
ops.add(Op.create(nodePath, null, zkClient.getZkACLProvider().getACLsToAdd(nodePath), CreateMode.EPHEMERAL));
// Create the roles node as well
- ops.add(Op.create(ZkStateReader.NODE_ROLES + "/" + nodeName,
- Utils.toJSON(cc.nodeRoles), zkClient.getZkACLProvider().getACLsToAdd(nodePath), CreateMode.EPHEMERAL));
+ cc.nodeRoles.ROLES.forEach((role, val) -> ops.add(Op.create(ZkStateReader.NODE_ROLES + "/" + role.roleName+ "/"+val +"/"+ nodeName,
+ null, zkClient.getZkACLProvider().getACLsToAdd(nodePath), CreateMode.EPHEMERAL)));
zkClient.multi(ops, true);
}
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/Assign.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/Assign.java
index 5a74d78..185acb3 100644
--- a/solr/core/src/java/org/apache/solr/cloud/api/collections/Assign.java
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/Assign.java
@@ -51,9 +51,9 @@ import org.apache.solr.common.cloud.Slice;
import org.apache.solr.common.cloud.ZkNodeProps;
import org.apache.solr.common.cloud.ZkStateReader;
import org.apache.solr.common.util.StrUtils;
-import org.apache.solr.common.util.Utils;
import org.apache.solr.core.CoreContainer;
-import org.apache.solr.core.NodeRole;
+import org.apache.solr.core.NodeRoles;
+import org.apache.solr.handler.ClusterAPI;
import org.apache.solr.util.NumberUtils;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
@@ -256,15 +256,10 @@ public class Assign {
public static void filterNonDataNodes(DistribStateManager zk, List<String> liveNodes) {
try {
- zk.forEachChild(ZkStateReader.NODE_ROLES, (name, data) -> {
- if (data != null && data.getData() != null && data.getData().length > 0) {
- @SuppressWarnings("unchecked")
- List<String> roles = (List<String>) Utils.fromJSON(data.getData());
- if (!roles.contains(NodeRole.Role.DATA.toString())) {
- liveNodes.remove(name);
- }
- }
- });
+ List<String> noData = ClusterAPI.getNodesByRole(NodeRoles.Role.DATA, NodeRoles.OFF, zk);
+ if(!noData.isEmpty()){
+ liveNodes.removeAll(noData);
+ }
} catch (Exception e) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Problem fetching roles from Zookeeper", e);
}
diff --git a/solr/core/src/java/org/apache/solr/core/CoreContainer.java b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
index 44c63db..f7e9a1f 100644
--- a/solr/core/src/java/org/apache/solr/core/CoreContainer.java
+++ b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
@@ -246,7 +246,7 @@ public class CoreContainer {
private final ObjectCache objectCache = new ObjectCache();
- public final NodeRole nodeRoles = new NodeRole(System.getProperty(NodeRole.NODE_ROLES_PROP));
+ public final NodeRoles nodeRoles = new NodeRoles(System.getProperty(NodeRoles.NODE_ROLES_PROP));
private final ClusterSingletons clusterSingletons = new ClusterSingletons(
() -> getZkController() != null &&
@@ -947,9 +947,9 @@ public class CoreContainer {
});
clusterSingletons.setReady();
- if (nodeRoles.getRoles().contains(NodeRole.Role.OVERSEER)) {
+ if (NodeRoles.PREFERRED.equals(nodeRoles.getRoleVal(NodeRoles.Role.OVERSEER))) {
try {
- log.info("This node is started as an overseer");
+ log.info("This node is started as a preferred overseer");
zkSys.getZkController().setPreferredOverseer();
} catch (KeeperException | InterruptedException e) {
throw new SolrException(ErrorCode.SERVER_ERROR, e);
diff --git a/solr/core/src/java/org/apache/solr/core/NodeRole.java b/solr/core/src/java/org/apache/solr/core/NodeRole.java
deleted file mode 100644
index a4cf757..0000000
--- a/solr/core/src/java/org/apache/solr/core/NodeRole.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF 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.
- */
-package org.apache.solr.core;
-
-import java.io.IOException;
-import java.lang.invoke.MethodHandles;
-import java.util.*;
-
-import org.apache.solr.common.IteratorWriter;
-import org.apache.solr.common.StringUtils;
-import org.apache.solr.common.util.StrUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class NodeRole implements IteratorWriter {
- private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
-
- public static final String NODE_ROLES_PROP = "solr.node.roles";
-
- private final Set<Role> roles;
-
- public NodeRole(String role) {
- if (StringUtils.isEmpty(role)) {
- // if no roles were specified, assume "data" role for backcompat reasons
- roles = Set.of(Role.DATA);
- return;
- }
- Set<String> rolesSet = new HashSet<>(StrUtils.split(role, ','));
- if (rolesSet.isEmpty()) {
- // if no roles were specified, assume "data" role for backcompat reasons
- roles = Set.of(Role.DATA);
- return;
- }
- roles = new TreeSet<>();
- for (String r: rolesSet) {
- roles.add(Role.valueOfCaseInsensitive(r));
- }
- }
-
- public Set<Role> getRoles() {
- return roles;
- }
-
- @Override
- public void writeIter(ItemWriter iw) throws IOException {
- for (Role role: roles) iw.add(role.toString());
- }
-
- public enum Role {
- DATA, OVERSEER;
-
- public static Role valueOfCaseInsensitive(String value) {
- // Given a user string "overseer", convert to OVERSEER and return the enum value
- String canonicalValue = value.toUpperCase().replace(' ', '_');
- return Role.valueOf(canonicalValue);
- }
-
- @Override
- public String toString() {
- return super.toString().toLowerCase();
- }
- }
-}
diff --git a/solr/core/src/java/org/apache/solr/core/NodeRoles.java b/solr/core/src/java/org/apache/solr/core/NodeRoles.java
new file mode 100644
index 0000000..fb3f011
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/core/NodeRoles.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.
+ */
+package org.apache.solr.core;
+
+import java.lang.invoke.MethodHandles;
+import java.util.*;
+import org.apache.solr.common.MapWriter;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.StringUtils;
+import org.apache.solr.common.util.StrUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class NodeRoles implements MapWriter {
+ private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ public static final String NODE_ROLES_PROP = "solr.node.roles";
+
+ public Map<Role, String> ROLES ;
+
+ public NodeRoles(String role) {
+ ROLES = new EnumMap<>(Role.class);
+ if (StringUtils.isEmpty(role)) {
+ role = DEFAULT_ROLE;
+ }
+ List<String> rolesList = StrUtils.splitSmart(role, ',');
+ for (String s : rolesList) {
+ List<String> roleVal = StrUtils.splitSmart(s,':');
+ Role r = Role.getRole(roleVal.get(0));
+ if(r.supportedVals().contains(roleVal.get(1))) {
+ ROLES.put(r, roleVal.get(1));
+ } else {
+ throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "UNKNOWN role value :"+roleVal.get(0));
+ }
+ }
+ ROLES = Collections.unmodifiableMap(ROLES);
+ }
+
+ public String getRoleVal(Role role) {
+ return ROLES.get(role);
+ }
+
+
+
+ @Override
+ public void writeMap(EntryWriter ew) {
+ ROLES.forEach((role, s) -> ew.putNoEx(role.roleName, s));
+ }
+
+ public enum Role {
+ DATA("data") ,
+ OVERSEER("overseer") {
+ @Override
+ public Set<String> supportedVals() {
+ return OVERSEER_VALS;
+ }
+ },
+ COORDINATOR("coordinator");
+
+ public final String roleName;
+
+ Role(String name) {
+ this.roleName = name;
+ }
+
+ public static Role getRole(String value) {
+ // Given a user string "overseer", convert to OVERSEER and return the enum value
+ for (Role role : Role.values()) {
+ if(value.equals(role.roleName)) return role;
+ }
+ throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown role : "+value);
+ }
+
+ public Set<String> supportedVals() {
+ return ON_OFF;
+
+ }
+
+ @Override
+ public String toString() {
+ return super.toString().toLowerCase();
+ }
+ }
+
+ public static final String ON = "on";
+ public static final String OFF = "off";
+ public static final Set<String> ON_OFF = Set.of(ON,OFF);
+ public static final String ALLOWED = "allowed";
+ public static final String DISALLOWED = "disallowed";
+ public static final String PREFERRED = "preferred";
+ public static final Set<String> OVERSEER_VALS = Set.of(ALLOWED, DISALLOWED, PREFERRED);
+ public static final String DEFAULT_ROLE = "data:on,overseer:allowed";
+}
diff --git a/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java b/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
index 5f512d2..796ac49 100644
--- a/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
+++ b/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
@@ -42,6 +42,7 @@ import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.util.ReflectMapWriter;
import org.apache.solr.common.util.Utils;
import org.apache.solr.core.CoreContainer;
+import org.apache.solr.core.NodeRoles;
import org.apache.solr.handler.admin.CollectionsHandler;
import org.apache.solr.handler.admin.ConfigSetsHandler;
import org.apache.solr.request.SolrQueryRequest;
@@ -63,20 +64,21 @@ import static org.apache.solr.security.PermissionNameProvider.Name.COLL_READ_PER
import static org.apache.solr.security.PermissionNameProvider.Name.CONFIG_EDIT_PERM;
import static org.apache.solr.security.PermissionNameProvider.Name.CONFIG_READ_PERM;
-/** All V2 APIs that have a prefix of /api/cluster/
- *
+/**
+ * All V2 APIs that have a prefix of /api/cluster/
*/
public class ClusterAPI {
private final CollectionsHandler collectionsHandler;
private final ConfigSetsHandler configSetsHandler;
- public final Commands commands = new Commands();
- public final ConfigSetCommands configSetCommands = new ConfigSetCommands();
+ public final Commands commands = new Commands();
+ public final ConfigSetCommands configSetCommands = new ConfigSetCommands();
public ClusterAPI(CollectionsHandler ch, ConfigSetsHandler configSetsHandler) {
this.collectionsHandler = ch;
this.configSetsHandler = configSetsHandler;
}
+
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@EndPoint(method = GET,
@@ -85,14 +87,45 @@ public class ClusterAPI {
@SuppressWarnings("unchecked")
// nocommit: it must also output all data nodes that didn't start with -Dsolr.node.roles
public void roles(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
- Map <String, List<String>> result = new LinkedHashMap<>();
- collectionsHandler.getCoreContainer().getZkController().getSolrCloudManager().getDistribStateManager().
- forEachChild(ZkStateReader.NODE_ROLES, (node, data) -> {
- if (data != null && data.getData() != null) {
- result.put(node, ((List<String>) Utils.fromJSON(data.getData())));
+ Map<String, Object> result = new LinkedHashMap<>();
+
+ rsp.add("node-roles", readRecursive(ZkStateReader.NODE_ROLES, collectionsHandler.getCoreContainer().getZkController().getSolrCloudManager().getDistribStateManager(), 3));
+ }
+
+ Object readRecursive(String path, DistribStateManager zk, int depth) {
+ if (depth == 0) return null;
+ Map<String, Object> result = null;
+ boolean hasSubValues = false;
+ try {
+ List<String> children = zk.listData(path);
+ if (children != null && !children.isEmpty()) {
+ result = new HashMap<>();
+ } else {
+ return depth >=1 ? Collections.emptyList(): null;
}
- });
- rsp.add("node-roles", result);
+ for (String child : children) {
+ Object c = readRecursive(path + "/" + child, zk, depth - 1);
+ if (c != null) {
+ hasSubValues = true;
+ }
+ result.put(child, c);
+ }
+
+
+ } catch (Exception e) {
+ }
+
+
+
+ if (result == null) {
+ return null;
+ }
+ if (depth == 1) {
+ return result.keySet();
+ } else {
+ return result;
+ }
+
}
@EndPoint(method = GET,
@@ -100,28 +133,35 @@ public class ClusterAPI {
permission = COLL_READ_PERM)
public void nodesWithRole(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
String role = req.getPathTemplateValues().get("role");
- rsp.add("nodes",
- getNodesByRole(role, collectionsHandler.getCoreContainer().getZkController().getSolrCloudManager().getDistribStateManager()));
+ rsp.add(role,
+ readRecursive(ZkStateReader.NODE_ROLES + "/"+ role,
+ collectionsHandler.getCoreContainer().getZkController().getSolrCloudManager().getDistribStateManager(), 2));
+ }
+ @EndPoint(method = GET,
+ path = "/cluster/node-roles/{role}/{role-val}",
+ permission = COLL_READ_PERM)
+ public void nodesWithRoleVal(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
+ String role = req.getPathTemplateValues().get("role");
+ String roleVal = req.getPathTemplateValues().get("role-val");
+ List<String> nodes = collectionsHandler.getCoreContainer().getZkController().getSolrCloudManager()
+ .getDistribStateManager().listData(ZkStateReader.NODE_ROLES + "/"+ role+"/"+roleVal);
+ rsp.add( role, Collections.singletonMap(roleVal, nodes));
}
- public static List<String> getNodesByRole(String role, DistribStateManager zk) throws InterruptedException, IOException, KeeperException {
- List<String> result = new ArrayList<>();
- zk.forEachChild(ZkStateReader.NODE_ROLES, (node, data) -> {
- if (data != null && data.getData() != null) {
- @SuppressWarnings("unchecked")
- List<String> rolesData = (List<String>) Utils.fromJSON(data.getData());
- if (rolesData.contains(role)) {
- result.add(node);
- }
- }
- });
- return result;
+ public static List<String> getNodesByRole(NodeRoles.Role role, String val, DistribStateManager zk)
+ throws InterruptedException, IOException, KeeperException {
+ try {
+ return zk.listData(ZkStateReader.NODE_ROLES+"/"+ role+ "/"+val);
+ } catch (NoSuchElementException e) {
+ return Collections.emptyList();
+ }
+
}
@EndPoint(method = GET,
- path = "/cluster/aliases",
- permission = COLL_READ_PERM)
+ path = "/cluster/aliases",
+ permission = COLL_READ_PERM)
public void aliases(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
final Map<String, Object> v1Params = Maps.newHashMap();
v1Params.put(ACTION, CollectionParams.CollectionAction.LISTALIASES.lowerName);
@@ -129,58 +169,58 @@ public class ClusterAPI {
}
@EndPoint(method = GET,
- path = "/cluster/overseer",
- permission = COLL_READ_PERM)
+ path = "/cluster/overseer",
+ permission = COLL_READ_PERM)
public void getOverseerStatus(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
collectionsHandler.handleRequestBody(wrapParams(req, "action", OVERSEERSTATUS.lowerName), rsp);
}
@EndPoint(method = GET,
- path = "/cluster",
- permission = COLL_READ_PERM)
+ path = "/cluster",
+ permission = COLL_READ_PERM)
public void getCluster(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
collectionsHandler.handleRequestBody(wrapParams(req, "action", LIST.lowerName), rsp);
}
@EndPoint(method = DELETE,
- path = "/cluster/command-status/{id}",
- permission = COLL_EDIT_PERM)
+ path = "/cluster/command-status/{id}",
+ permission = COLL_EDIT_PERM)
public void deleteCommandStatus(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
final Map<String, Object> v1Params = Maps.newHashMap();
v1Params.put(ACTION, DELETESTATUS.lowerName);
v1Params.put(REQUESTID, req.getPathTemplateValues().get("id"));
collectionsHandler.handleRequestBody(wrapParams(req, v1Params), rsp);
}
-
+
@EndPoint(method = DELETE,
- path = "/cluster/command-status",
- permission = COLL_EDIT_PERM)
+ path = "/cluster/command-status",
+ permission = COLL_EDIT_PERM)
public void flushCommandStatus(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
CollectionsHandler.CollectionOperation.DELETESTATUS_OP.execute(req, rsp, collectionsHandler);
}
@EndPoint(method = DELETE,
- path = "/cluster/configs/{name}",
- permission = CONFIG_EDIT_PERM
+ path = "/cluster/configs/{name}",
+ permission = CONFIG_EDIT_PERM
)
public void deleteConfigSet(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
req = wrapParams(req,
- "action", ConfigSetParams.ConfigSetAction.DELETE.toString(),
- CommonParams.NAME, req.getPathTemplateValues().get("name"));
+ "action", ConfigSetParams.ConfigSetAction.DELETE.toString(),
+ CommonParams.NAME, req.getPathTemplateValues().get("name"));
configSetsHandler.handleRequestBody(req, rsp);
}
@EndPoint(method = GET,
- path = "/cluster/configs",
- permission = CONFIG_READ_PERM)
+ path = "/cluster/configs",
+ permission = CONFIG_READ_PERM)
public void listConfigSet(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
req = wrapParams(req, "action", ConfigSetParams.ConfigSetAction.LIST.toString());
configSetsHandler.handleRequestBody(req, rsp);
}
@EndPoint(method = POST,
- path = "/cluster/configs",
- permission = CONFIG_EDIT_PERM
+ path = "/cluster/configs",
+ permission = CONFIG_EDIT_PERM
)
public class ConfigSetCommands {
@@ -188,8 +228,8 @@ public class ClusterAPI {
@SuppressWarnings("unchecked")
public void create(PayloadObj<CreateConfigPayload> obj) throws Exception {
Map<String, Object> mapVals = obj.get().toMap(new HashMap<>());
- Map<String,Object> customProps = obj.get().properties;
- if(customProps!= null) {
+ Map<String, Object> customProps = obj.get().properties;
+ if (customProps != null) {
customProps.forEach((k, o) -> mapVals.put(ConfigSetCmds.CONFIG_SET_PROPERTY_PREFIX + k, o));
}
mapVals.put("action", ConfigSetParams.ConfigSetAction.CREATE.toString());
@@ -199,8 +239,8 @@ public class ClusterAPI {
}
@EndPoint(method = PUT,
- path = "/cluster/configs/{name}",
- permission = CONFIG_EDIT_PERM
+ path = "/cluster/configs/{name}",
+ permission = CONFIG_EDIT_PERM
)
public void uploadConfigSet(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
req = wrapParams(req,
@@ -212,8 +252,8 @@ public class ClusterAPI {
}
@EndPoint(method = PUT,
- path = "/cluster/configs/{name}/*",
- permission = CONFIG_EDIT_PERM
+ path = "/cluster/configs/{name}/*",
+ permission = CONFIG_EDIT_PERM
)
public void insertIntoConfigSet(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
String path = req.getPathTemplateValues().get("*");
@@ -236,17 +276,17 @@ public class ClusterAPI {
public static SolrQueryRequest wrapParams(SolrQueryRequest req, Map<String, Object> m) {
ModifiableSolrParams solrParams = new ModifiableSolrParams();
m.forEach((k, v) -> {
- if(v == null) return;
+ if (v == null) return;
solrParams.add(k.toString(), String.valueOf(v));
});
- DefaultSolrParams dsp = new DefaultSolrParams(req.getParams(),solrParams);
+ DefaultSolrParams dsp = new DefaultSolrParams(req.getParams(), solrParams);
req.setParams(dsp);
return req;
}
@EndPoint(method = GET,
- path = "/cluster/command-status/{id}",
- permission = COLL_READ_PERM)
+ path = "/cluster/command-status/{id}",
+ permission = COLL_READ_PERM)
public void getCommandStatus(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
final Map<String, Object> v1Params = Maps.newHashMap();
v1Params.put(ACTION, REQUESTSTATUS.lowerName);
@@ -255,8 +295,8 @@ public class ClusterAPI {
}
@EndPoint(method = GET,
- path = "/cluster/nodes",
- permission = COLL_READ_PERM)
+ path = "/cluster/nodes",
+ permission = COLL_READ_PERM)
public void getNodes(SolrQueryRequest req, SolrQueryResponse rsp) {
rsp.add("nodes", getCoreContainer().getZkController().getClusterState().getLiveNodes());
}
@@ -266,13 +306,13 @@ public class ClusterAPI {
}
@EndPoint(method = POST,
- path = "/cluster",
- permission = COLL_EDIT_PERM)
+ path = "/cluster",
+ permission = COLL_EDIT_PERM)
public class Commands {
@Command(name = "add-role")
public void addRole(PayloadObj<RoleInfo> obj) throws Exception {
RoleInfo info = obj.get();
- Map<String,Object> m = info.toMap(new HashMap<>());
+ Map<String, Object> m = info.toMap(new HashMap<>());
m.put("action", ADDROLE.toString());
collectionsHandler.handleRequestBody(wrapParams(obj.getRequest(), m), obj.getResponse());
}
@@ -280,7 +320,7 @@ public class ClusterAPI {
@Command(name = "remove-role")
public void removeRole(PayloadObj<RoleInfo> obj) throws Exception {
RoleInfo info = obj.get();
- Map<String,Object> m = info.toMap(new HashMap<>());
+ Map<String, Object> m = info.toMap(new HashMap<>());
m.put("action", REMOVEROLE.toString());
collectionsHandler.handleRequestBody(wrapParams(obj.getRequest(), m), obj.getResponse());
}
@@ -288,7 +328,7 @@ public class ClusterAPI {
@Command(name = "set-obj-property")
public void setObjProperty(PayloadObj<ClusterPropPayload> obj) {
//Not using the object directly here because the API differentiate between {name:null} and {}
- Map<String,Object> m = obj.getDataMap();
+ Map<String, Object> m = obj.getDataMap();
ClusterProperties clusterProperties = new ClusterProperties(getCoreContainer().getZkController().getZkClient());
try {
clusterProperties.setClusterProperties(m);
@@ -298,8 +338,8 @@ public class ClusterAPI {
}
@Command(name = "set-property")
- public void setProperty(PayloadObj<Map<String,String>> obj) throws Exception {
- Map<String,Object> m = obj.getDataMap();
+ public void setProperty(PayloadObj<Map<String, String>> obj) throws Exception {
+ Map<String, Object> m = obj.getDataMap();
m.put("action", CLUSTERPROP.toString());
collectionsHandler.handleRequestBody(wrapParams(obj.getRequest(), m), obj.getResponse());
}
diff --git a/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java b/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java
index e5f2ee1..18a16fe 100644
--- a/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java
@@ -18,14 +18,14 @@
package org.apache.solr.cloud;
import java.lang.invoke.MethodHandles;
-import java.util.List;
-import java.util.TreeSet;
+import java.util.Collection;
+import java.util.Collections;
import org.apache.solr.client.solrj.embedded.JettySolrRunner;
import org.apache.solr.client.solrj.request.CollectionAdminRequest;
import org.apache.solr.client.solrj.request.V2Request;
import org.apache.solr.client.solrj.response.V2Response;
-import org.apache.solr.core.NodeRole;
+import org.apache.solr.core.NodeRoles;
import org.junit.After;
import org.junit.Before;
import org.slf4j.Logger;
@@ -51,20 +51,34 @@ public class NodeRolesTest extends SolrCloudTestCase {
JettySolrRunner j0 = cluster.getJettySolrRunner(0);
JettySolrRunner j1 = null, j2 = null;
- j1 = startNodeWithRoles("overseer");
+ j1 = startNodeWithRoles("overseer:preferred,data:off");
V2Response rsp = new V2Request.Builder("/cluster/node-roles").GET().build().process(cluster.getSolrClient());
- assertEquals(List.of("overseer"), rsp._get(List.of("node-roles", j1.getNodeName()), null));
-
+ assertEquals(j1.getNodeName(), rsp._getStr("node-roles/overseer/preferred[0]", null));
+ assertEquals(j1.getNodeName(), rsp._getStr("node-roles/data/off[0]", null));
OverseerRolesTest.waitForNewOverseer(20, j1.getNodeName(), false);
//start another node that is overseer but has data
- j2 = startNodeWithRoles("overseer,data");
+ j2 = startNodeWithRoles("overseer:preferred,data:on");
rsp = new V2Request.Builder("/cluster/node-roles").GET().build().process(cluster.getSolrClient());
- assertEquals(List.of("data", "overseer"), rsp._get(List.of("node-roles", j2.getNodeName()), null));
+
+
+ assertTrue( ((Collection)rsp._get("node-roles/overseer/preferred", Collections.emptyList())).contains(j2.getNodeName()));
+ assertTrue( ((Collection)rsp._get("node-roles/data/on", Collections.emptyList())).contains(j2.getNodeName()));
+
+
rsp = new V2Request.Builder("/cluster/node-roles/overseer").GET().build().process(cluster.getSolrClient());
- assertEquals(new TreeSet<String>(List.of(j1.getNodeName(), j2.getNodeName())), new TreeSet<String>((List<String>) rsp._get("nodes", null)));
+
+
+ assertTrue( ((Collection)rsp._get("overseer/preferred", Collections.emptyList())).contains(j2.getNodeName()));
+ assertTrue( ((Collection)rsp._get("overseer/preferred", Collections.emptyList())).contains(j1.getNodeName()));
+
+ rsp = new V2Request.Builder("/cluster/node-roles/overseer/preferred").GET().build().process(cluster.getSolrClient());
+ assertTrue( ((Collection)rsp._get("overseer/preferred", Collections.emptyList())).contains(j2.getNodeName()));
+ assertTrue( ((Collection)rsp._get("overseer/preferred", Collections.emptyList())).contains(j1.getNodeName()));
+
+
String COLLECTION_NAME = "TEST_ROLES";
CollectionAdminRequest
@@ -79,17 +93,17 @@ public class NodeRolesTest extends SolrCloudTestCase {
// Shutdown the dedicated overseer, make sure that node disappears from the roles output
j1.stop();
- rsp = new V2Request.Builder("/cluster/node-roles").GET().build().process(cluster.getSolrClient());
- assertNull(rsp._get(List.of("node-roles", dedicatedOverseer), null));
+ rsp = new V2Request.Builder("/cluster/node-roles/overseer/preferred").GET().build().process(cluster.getSolrClient());
+ assertFalse (((Collection) rsp._get("overseer/preferred" , null)). contains(j1.getNodeName()));
}
private JettySolrRunner startNodeWithRoles(String roles) throws Exception {
JettySolrRunner jetty;
- System.setProperty(NodeRole.NODE_ROLES_PROP, roles);
+ System.setProperty(NodeRoles.NODE_ROLES_PROP, roles);
try {
jetty = cluster.startJettySolrRunner();
} finally {
- System.clearProperty(NodeRole.NODE_ROLES_PROP);
+ System.clearProperty(NodeRoles.NODE_ROLES_PROP);
}
return jetty;
}
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/DistribStateManager.java b/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/DistribStateManager.java
index 84010c8..feae27f 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/DistribStateManager.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/DistribStateManager.java
@@ -24,10 +24,8 @@ import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;
-import java.util.function.BiConsumer;
import org.apache.solr.common.SolrCloseable;
import org.apache.solr.common.cloud.PerReplicaStates;
-import org.apache.solr.common.cloud.ZkStateReader;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.Op;
@@ -143,25 +141,4 @@ public interface DistribStateManager extends SolrCloseable {
}
}
}
- default void forEachChild(String path, BiConsumer<String, VersionedData> fun)
- throws InterruptedException, IOException, KeeperException {
- List<String> children = null;
- try {
- children = listData(path);
- } catch (NoSuchElementException e) {
- return;
- }
- if (!children.isEmpty()) {
- for (String node: children) {
- VersionedData data = null;
- try {
- data = getData(path + "/" + node);
- } catch (NoSuchElementException e) {
- // this node must've been deleted
- continue;
- }
- fun.accept(node, data);
- }
- }
- }
}
[solr] 16/28: clanup
Posted by is...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
ishan pushed a commit to branch jira/SOLR15694
in repository https://gitbox.apache.org/repos/asf/solr.git
commit c941fd8e40c55e579ba34cbf422bd60710a77514
Author: Noble Paul <no...@gmail.com>
AuthorDate: Mon Dec 13 14:35:30 2021 +1100
clanup
---
solr/core/src/java/org/apache/solr/core/NodeRoles.java | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/solr/core/src/java/org/apache/solr/core/NodeRoles.java b/solr/core/src/java/org/apache/solr/core/NodeRoles.java
index 4bb04e0..69c2572 100644
--- a/solr/core/src/java/org/apache/solr/core/NodeRoles.java
+++ b/solr/core/src/java/org/apache/solr/core/NodeRoles.java
@@ -36,6 +36,7 @@ public class NodeRoles implements MapWriter {
public static final String DISALLOWED = "disallowed";
public static final String PREFERRED = "preferred";
public static final Set<String> OVERSEER_MODES = Set.of(ALLOWED, PREFERRED, DISALLOWED);
+ public static final Set<String> ON_OFF = Set.of(ON,OFF);
public static final String DEFAULT_ROLES_STRING = "data:on,overseer:allowed";
@@ -110,7 +111,7 @@ public class NodeRoles implements MapWriter {
}
public Set<String> supportedModes() {
- return Set.of(ON, OFF);
+ return ON_OFF;
}
/**
[solr] 02/28: license header
Posted by is...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
ishan pushed a commit to branch jira/SOLR15694
in repository https://gitbox.apache.org/repos/asf/solr.git
commit 277e4156cbb067f20ef6f588fdb6aa3b78d352d3
Author: Noble Paul <no...@gmail.com>
AuthorDate: Wed Nov 10 00:12:44 2021 +1100
license header
---
.../core/src/test/org/apache/solr/core/TestNodeRole.java | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/solr/core/src/test/org/apache/solr/core/TestNodeRole.java b/solr/core/src/test/org/apache/solr/core/TestNodeRole.java
index d5bdba5..b328983 100644
--- a/solr/core/src/test/org/apache/solr/core/TestNodeRole.java
+++ b/solr/core/src/test/org/apache/solr/core/TestNodeRole.java
@@ -1,3 +1,19 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.
+ */
package org.apache.solr.core;
import java.lang.invoke.MethodHandles;
[solr] 22/28: Fixing precommit
Posted by is...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
ishan pushed a commit to branch jira/SOLR15694
in repository https://gitbox.apache.org/repos/asf/solr.git
commit 3c17462d000b47c3ffd79cc60021f6eb030dfc9f
Author: Ishan Chattopadhyaya <is...@apache.org>
AuthorDate: Fri Dec 17 14:30:16 2021 +0530
Fixing precommit
---
solr/core/src/java/org/apache/solr/core/NodeRoles.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/solr/core/src/java/org/apache/solr/core/NodeRoles.java b/solr/core/src/java/org/apache/solr/core/NodeRoles.java
index 69c2572..71c0303 100644
--- a/solr/core/src/java/org/apache/solr/core/NodeRoles.java
+++ b/solr/core/src/java/org/apache/solr/core/NodeRoles.java
@@ -123,7 +123,7 @@ public class NodeRoles implements MapWriter {
@Override
public String toString() {
- return super.toString().toLowerCase();
+ return super.toString().toLowerCase(Locale.ROOT);
}
}
}
[solr] 26/28: Addressing review feedback
Posted by is...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
ishan pushed a commit to branch jira/SOLR15694
in repository https://gitbox.apache.org/repos/asf/solr.git
commit 50f40a1cbf98c049eb6fd9a89c814770a8de4513
Author: Ishan Chattopadhyaya <is...@apache.org>
AuthorDate: Wed Dec 22 07:15:00 2021 +0530
Addressing review feedback
---
.../apache/solr/cloud/OverseerNodePrioritizer.java | 10 ++-
.../java/org/apache/solr/cloud/ZkController.java | 14 ++--
.../apache/solr/cloud/api/collections/Assign.java | 4 +-
.../java/org/apache/solr/core/CoreContainer.java | 4 +-
.../src/java/org/apache/solr/core/NodeRoles.java | 93 ++++++++++++++--------
.../java/org/apache/solr/handler/ClusterAPI.java | 40 +++++-----
.../test/org/apache/solr/cloud/NodeRolesTest.java | 2 +-
solr/solr-ref-guide/src/node-roles.adoc | 2 +-
.../apache/solr/common/cloud/ZkStateReader.java | 10 ++-
9 files changed, 110 insertions(+), 69 deletions(-)
diff --git a/solr/core/src/java/org/apache/solr/cloud/OverseerNodePrioritizer.java b/solr/core/src/java/org/apache/solr/cloud/OverseerNodePrioritizer.java
index 4bd21dd..db162a1 100644
--- a/solr/core/src/java/org/apache/solr/cloud/OverseerNodePrioritizer.java
+++ b/solr/core/src/java/org/apache/solr/cloud/OverseerNodePrioritizer.java
@@ -73,7 +73,15 @@ public class OverseerNodePrioritizer {
}
}
- overseerDesignates.addAll(ClusterAPI.getNodesByRole(NodeRoles.Role.OVERSEER, NodeRoles.PREFERRED, new ZkDistribStateManager(zkStateReader.getZkClient())));
+ List<String> preferredOverseers = ClusterAPI.getNodesByRole(NodeRoles.Role.OVERSEER, NodeRoles.Mode.PREFERRED,
+ new ZkDistribStateManager(zkStateReader.getZkClient()));
+ for (String preferred: preferredOverseers) {
+ if (overseerDesignates.contains(preferred)) {
+ log.warn("Node " + preferred + " has been configured to be a preferred overseer using both ADDROLE API command " +
+ "as well as using Node Roles (i.e. -Dsolr.node.roles start up property). Only the latter is recommended.");
+ }
+ }
+ overseerDesignates.addAll(preferredOverseers);
if (overseerDesignates.isEmpty()) return;
String ldr = OverseerTaskProcessor.getLeaderNode(zk);
if(overseerDesignates.contains(ldr)) return;
diff --git a/solr/core/src/java/org/apache/solr/cloud/ZkController.java b/solr/core/src/java/org/apache/solr/cloud/ZkController.java
index ee0c87f..8688bfd 100644
--- a/solr/core/src/java/org/apache/solr/cloud/ZkController.java
+++ b/solr/core/src/java/org/apache/solr/cloud/ZkController.java
@@ -358,7 +358,7 @@ public class ZkController implements Closeable {
overseerElector.setup(context);
- if(cc.nodeRoles.isOverseerAllowed()) {
+ if (cc.nodeRoles.isOverseerAllowedOrPreferred()) {
overseerElector.joinElection(context, true);
}
}
@@ -858,10 +858,10 @@ public class ZkController implements Closeable {
ZkCmdExecutor cmdExecutor = new ZkCmdExecutor(zkClient.getZkClientTimeout());
cmdExecutor.ensureExists(ZkStateReader.LIVE_NODES_ZKNODE, zkClient);
cmdExecutor.ensureExists(ZkStateReader.NODE_ROLES, zkClient);
- for (NodeRoles.Role role : NodeRoles.Role.values()) {
- cmdExecutor.ensureExists(ZkStateReader.NODE_ROLES + "/" + role.roleName, zkClient);
- for (String v : role.supportedModes()) {
- cmdExecutor.ensureExists(ZkStateReader.NODE_ROLES + "/" + role.roleName + "/"+v, zkClient);
+ for (NodeRoles.Role role: NodeRoles.Role.values()) {
+ cmdExecutor.ensureExists(NodeRoles.getZNodeForRole(role), zkClient);
+ for (NodeRoles.Mode mode: role.supportedModes()) {
+ cmdExecutor.ensureExists(NodeRoles.getZNodeForRoleMode(role, mode), zkClient);
}
}
@@ -924,7 +924,7 @@ public class ZkController implements Closeable {
ElectionContext context = new OverseerElectionContext(zkClient,
overseer, getNodeName());
overseerElector.setup(context);
- if(cc.nodeRoles.isOverseerAllowed()) {
+ if (cc.nodeRoles.isOverseerAllowedOrPreferred()) {
overseerElector.joinElection(context, false);
}
}
@@ -1101,7 +1101,7 @@ public class ZkController implements Closeable {
ops.add(Op.create(nodePath, null, zkClient.getZkACLProvider().getACLsToAdd(nodePath), CreateMode.EPHEMERAL));
// Create the roles node as well
- cc.nodeRoles.getRoles().forEach((role, val) -> ops.add(Op.create(ZkStateReader.NODE_ROLES + "/" + role.roleName+ "/"+val +"/"+ nodeName,
+ cc.nodeRoles.getRoles().forEach((role, mode) -> ops.add(Op.create(NodeRoles.getZNodeForRoleMode(role, mode) +"/" + nodeName,
null, zkClient.getZkACLProvider().getACLsToAdd(nodePath), CreateMode.EPHEMERAL)));
zkClient.multi(ops, true);
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/Assign.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/Assign.java
index 185acb3..7dfcc33 100644
--- a/solr/core/src/java/org/apache/solr/cloud/api/collections/Assign.java
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/Assign.java
@@ -256,8 +256,8 @@ public class Assign {
public static void filterNonDataNodes(DistribStateManager zk, List<String> liveNodes) {
try {
- List<String> noData = ClusterAPI.getNodesByRole(NodeRoles.Role.DATA, NodeRoles.OFF, zk);
- if(!noData.isEmpty()){
+ List<String> noData = ClusterAPI.getNodesByRole(NodeRoles.Role.DATA, NodeRoles.Mode.OFF, zk);
+ if (!noData.isEmpty()) {
liveNodes.removeAll(noData);
}
} catch (Exception e) {
diff --git a/solr/core/src/java/org/apache/solr/core/CoreContainer.java b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
index 72a0df3..e272767 100644
--- a/solr/core/src/java/org/apache/solr/core/CoreContainer.java
+++ b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
@@ -947,9 +947,9 @@ public class CoreContainer {
});
clusterSingletons.setReady();
- if (NodeRoles.PREFERRED.equals(nodeRoles.getRoleMode(NodeRoles.Role.OVERSEER))) {
+ if (NodeRoles.Mode.PREFERRED.equals(nodeRoles.getRoleMode(NodeRoles.Role.OVERSEER))) {
try {
- log.info("This node is started as a preferred overseer");
+ log.info("This node has been started as a preferred overseer");
zkSys.getZkController().setPreferredOverseer();
} catch (KeeperException | InterruptedException e) {
throw new SolrException(ErrorCode.SERVER_ERROR, e);
diff --git a/solr/core/src/java/org/apache/solr/core/NodeRoles.java b/solr/core/src/java/org/apache/solr/core/NodeRoles.java
index 71c0303..714d8b1 100644
--- a/solr/core/src/java/org/apache/solr/core/NodeRoles.java
+++ b/solr/core/src/java/org/apache/solr/core/NodeRoles.java
@@ -21,30 +21,29 @@ import java.util.*;
import org.apache.solr.common.MapWriter;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.StringUtils;
+import org.apache.solr.common.cloud.ZkStateReader;
import org.apache.solr.common.util.StrUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class NodeRoles implements MapWriter {
+public class NodeRoles {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
public static final String NODE_ROLES_PROP = "solr.node.roles";
- public static final String ON = "on";
+ /*public static final String ON = "on";
public static final String OFF = "off";
public static final String ALLOWED = "allowed";
public static final String DISALLOWED = "disallowed";
- public static final String PREFERRED = "preferred";
- public static final Set<String> OVERSEER_MODES = Set.of(ALLOWED, PREFERRED, DISALLOWED);
- public static final Set<String> ON_OFF = Set.of(ON,OFF);
+ public static final String PREFERRED = "preferred";*/
public static final String DEFAULT_ROLES_STRING = "data:on,overseer:allowed";
// Map of roles to mode that are applicable for this node.
- private Map<Role, String> nodeRoles;
+ private Map<Role, Mode> nodeRoles;
public NodeRoles(String rolesString) {
- Map<Role, String> roles = new EnumMap<>(Role.class);
+ Map<Role, Mode> roles = new EnumMap<>(Role.class);
if (StringUtils.isEmpty(rolesString)) {
rolesString = DEFAULT_ROLES_STRING;
}
@@ -52,10 +51,12 @@ public class NodeRoles implements MapWriter {
for (String s: rolesList) {
List<String> roleMode = StrUtils.splitSmart(s,':');
Role r = Role.getRole(roleMode.get(0));
- if (r.supportedModes().contains(roleMode.get(1))) {
- roles.put(r, roleMode.get(1));
+ Mode m = Mode.valueOf(roleMode.get(1).toUpperCase(Locale.ROOT));
+ if (r.supportedModes().contains(m)) {
+ roles.put(r, m);
} else {
- throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown role mode: " + roleMode.get(0));
+ throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
+ "Unknown role mode '" + roleMode.get(1) + "' for role '" + r + "'");
}
}
for(Role r: Role.values()) {
@@ -66,34 +67,51 @@ public class NodeRoles implements MapWriter {
nodeRoles = Collections.unmodifiableMap(roles);
}
- public Map<Role, String> getRoles() {
+ public Map<Role, Mode> getRoles() {
return nodeRoles;
}
- public String getRoleMode(Role role) {
+ public Mode getRoleMode(Role role) {
return nodeRoles.get(role);
}
- @Override
- public void writeMap(EntryWriter ew) {
- nodeRoles.forEach((role, s) -> ew.putNoEx(role.roleName, s));
+ public boolean isOverseerAllowedOrPreferred() {
+ Mode roleMode = nodeRoles.get(Role.OVERSEER);
+ return Mode.ALLOWED.equals(roleMode) || Mode.PREFERRED.equals(roleMode);
}
- public boolean isOverseerAllowed() {
- String roleMode = nodeRoles.get(Role.OVERSEER);
- return ALLOWED.equals(roleMode) || PREFERRED.equals(roleMode);
- }
+ public enum Mode {
+ ON, OFF, ALLOWED, PREFERRED, DISALLOWED;
+
+ @Override
+ /**
+ * Need this lowercasing so that the ZK references use the lowercase form, which is
+ * also the form documented in user facing documentation.
+ */
+ public String toString() {
+ return name().toLowerCase(Locale.ROOT);
+ }
+ };
public enum Role {
- DATA("data"),
+ DATA("data") {
+ @Override
+ public Set<Mode> supportedModes() {
+ return Set.of(Mode.ON, Mode.OFF);
+ }
+ @Override
+ public Mode defaultIfAbsent() {
+ return Mode.OFF;
+ }
+ },
OVERSEER("overseer") {
@Override
- public Set<String> supportedModes() {
- return OVERSEER_MODES;
+ public Set<Mode> supportedModes() {
+ return Set.of(Mode.ALLOWED, Mode.PREFERRED, Mode.DISALLOWED);
}
@Override
- public String defaultIfAbsent() {
- return DISALLOWED;
+ public Mode defaultIfAbsent() {
+ return Mode.DISALLOWED;
}
};
@@ -104,26 +122,33 @@ public class NodeRoles implements MapWriter {
}
public static Role getRole(String value) {
- for (Role role: Role.values()) {
- if (value.equals(role.roleName)) return role;
+ try {
+ Role role = Role.valueOf(value.toUpperCase(Locale.ROOT));
+ return role;
+ } catch (IllegalArgumentException ex) {
+ throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown role: " + value);
}
- throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown role: " + value);
}
- public Set<String> supportedModes() {
- return ON_OFF;
- }
+ public abstract Set<Mode> supportedModes();
/**
* Default mode for a role in nodes where this role is not specified.
*/
- public String defaultIfAbsent() {
- return OFF;
- }
+ public abstract Mode defaultIfAbsent();
@Override
public String toString() {
- return super.toString().toLowerCase(Locale.ROOT);
+ return roleName;
}
}
+
+ public static String getZNodeForRole(Role role) {
+ return ZkStateReader.NODE_ROLES + "/" + role.roleName;
+ }
+
+ public static String getZNodeForRoleMode(Role role, Mode mode) {
+ return ZkStateReader.NODE_ROLES + "/" + role.roleName + "/" + mode;
+ }
+
}
diff --git a/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java b/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
index 6a0148e..dce5ea5 100644
--- a/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
+++ b/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
@@ -20,6 +20,7 @@ package org.apache.solr.handler;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.util.*;
+import java.util.stream.Collectors;
import com.google.common.collect.Maps;
import org.apache.solr.api.Command;
@@ -85,23 +86,21 @@ public class ClusterAPI {
path = "/cluster/node-roles",
permission = COLL_READ_PERM)
public void roles(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
- Map<String, Object> result = new LinkedHashMap<>();
-
rsp.add("node-roles", readRecursive(ZkStateReader.NODE_ROLES,
collectionsHandler.getCoreContainer().getZkController().getSolrCloudManager().getDistribStateManager(), 3));
}
Object readRecursive(String path, DistribStateManager zk, int depth) {
if (depth == 0) return null;
- Map<String, Object> result = null;
+ Map<String, Object> result;
try {
List<String> children = zk.listData(path);
if (children != null && !children.isEmpty()) {
result = new HashMap<>();
} else {
- return depth >= 1 ? Collections.emptyList() : null;
+ return depth >= 1 ? Collections.emptySet(): null;
}
- for (String child : children) {
+ for (String child: children) {
Object c = readRecursive(path + "/" + child, zk, depth - 1);
result.put(child, c);
}
@@ -121,7 +120,7 @@ public class ClusterAPI {
public void nodesWithRole(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
String role = req.getPathTemplateValues().get("role");
rsp.add("node-roles", Map.of(role,
- readRecursive(ZkStateReader.NODE_ROLES + "/"+ role,
+ readRecursive(ZkStateReader.NODE_ROLES + "/" + role,
collectionsHandler.getCoreContainer().getZkController().getSolrCloudManager().getDistribStateManager(), 2)));
}
@@ -135,10 +134,8 @@ public class ClusterAPI {
Map<String, Map<String, Set<String>>> roles = (Map<String, Map<String, Set<String>>>) readRecursive(ZkStateReader.NODE_ROLES,
collectionsHandler.getCoreContainer().getZkController().getSolrCloudManager().getDistribStateManager(), 3);
for (String role: roles.keySet()) {
- for (String mode : roles.get(role).keySet()) {
- if (roles.get(role).get(mode) instanceof List && ((List) roles.get(role).get(mode)).size() == 0) {
- continue;
- }
+ for (String mode: roles.get(role).keySet()) {
+ if (roles.get(role).get(mode).isEmpty()) continue;
Set<String> nodes = roles.get(role).get(mode);
if (nodes.contains(node)) ret.put(role, mode);
}
@@ -154,30 +151,33 @@ public class ClusterAPI {
public void supportedRoles(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
Map<String, Object> roleModesSupportedMap = new HashMap<>();
for (NodeRoles.Role role: NodeRoles.Role.values()) {
- roleModesSupportedMap.put(role.toString(), Map.of("modes", role.supportedModes(), "defaultIfAbsent", role.defaultIfAbsent()));
+ roleModesSupportedMap.put(role.toString(),
+ Map.of("modes", (role.supportedModes()).stream().map(NodeRoles.Mode::toString).collect(Collectors.toList())));
}
rsp.add("supported-roles", roleModesSupportedMap);
}
@EndPoint(method = GET,
- path = "/cluster/node-roles/role/{role}/{role-val}",
+ path = "/cluster/node-roles/role/{role}/{mode}",
permission = COLL_READ_PERM)
- public void nodesWithRoleVal(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
- String role = req.getPathTemplateValues().get("role");
- String roleVal = req.getPathTemplateValues().get("role-val");
+ public void nodesWithRoleMode(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
+ // Here, deal with raw strings instead of Role & Mode types so as to handle roles and modes
+ // that are not understood by this node (possibly during a rolling upgrade)
+ String roleStr = req.getPathTemplateValues().get("role");
+ String modeStr = req.getPathTemplateValues().get("mode");
+
List<String> nodes = collectionsHandler.getCoreContainer().getZkController().getSolrCloudManager()
- .getDistribStateManager().listData(ZkStateReader.NODE_ROLES + "/"+ role+"/"+roleVal);
- rsp.add( "node-roles", Map.of(role, Collections.singletonMap(roleVal, nodes)));
+ .getDistribStateManager().listData(ZkStateReader.NODE_ROLES + "/" + roleStr + "/" + modeStr);
+ rsp.add( "node-roles", Map.of(roleStr, Collections.singletonMap(modeStr, nodes)));
}
- public static List<String> getNodesByRole(NodeRoles.Role role, String val, DistribStateManager zk)
+ public static List<String> getNodesByRole(NodeRoles.Role role, NodeRoles.Mode mode, DistribStateManager zk)
throws InterruptedException, IOException, KeeperException {
try {
- return zk.listData(ZkStateReader.NODE_ROLES+"/"+ role+ "/"+val);
+ return zk.listData(ZkStateReader.NODE_ROLES + "/" + role + "/" + mode);
} catch (NoSuchElementException e) {
return Collections.emptyList();
}
-
}
diff --git a/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java b/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java
index 63f7d86..0fc0e95 100644
--- a/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java
@@ -52,7 +52,7 @@ public class NodeRolesTest extends SolrCloudTestCase {
@SuppressWarnings("unchecked")
public void testRoleIntegration() throws Exception {
JettySolrRunner j0 = cluster.getJettySolrRunner(0);
- JettySolrRunner j1 = null, j2 = null;
+ JettySolrRunner j1, j2;
V2Response rsp = new V2Request.Builder("/cluster/node-roles/supported").GET().build().process(cluster.getSolrClient());
Map<String, Object> l = (Map<String, Object>) rsp._get("supported-roles", Collections.emptyMap());
assertTrue(l.containsKey("data"));
diff --git a/solr/solr-ref-guide/src/node-roles.adoc b/solr/solr-ref-guide/src/node-roles.adoc
index e5a70ec..455de14 100644
--- a/solr/solr-ref-guide/src/node-roles.adoc
+++ b/solr/solr-ref-guide/src/node-roles.adoc
@@ -45,7 +45,7 @@ Examples: `-Dsolr.node.roles=data:on,overseer:allowed` or `-Dsolr.node.roles=ove
[TIP]
====
-If a node has been started with no `solr.node.roles` parameter, it will be assumed to have the data role turned on and overseer role allowed on it. If you've never used roles before, you likely won't need to change anything in your startup parameters to accomodate the functionality associated with these roles.
+If a node has been started with no `solr.node.roles` parameter, it will be assumed to have the data role turned on and overseer role allowed on it. If you've never used roles before, you likely won't need to change anything in your startup parameters to accommodate the functionality associated with these roles.
====
.Supported roles
diff --git a/solr/solrj/src/java/org/apache/solr/common/cloud/ZkStateReader.java b/solr/solrj/src/java/org/apache/solr/common/cloud/ZkStateReader.java
index a01cab2..8814cc9 100644
--- a/solr/solrj/src/java/org/apache/solr/common/cloud/ZkStateReader.java
+++ b/solr/solrj/src/java/org/apache/solr/common/cloud/ZkStateReader.java
@@ -103,7 +103,16 @@ public class ZkStateReader implements SolrCloseable {
public static final String STATE_TIMESTAMP_PROP = "stateTimestamp";
public static final String COLLECTIONS_ZKNODE = "/collections";
public static final String LIVE_NODES_ZKNODE = "/live_nodes";
+
+ /**
+ * The following, node_roles and roles.json are for assigning roles to
+ * nodes. The node_roles is the preferred way (using -Dsolr.node.roles param),
+ * and roles.json is used by legacy ADDROLE API command.
+ * TODO: Deprecate & remove support for roles.json in an upcoming release.
+ */
public static final String NODE_ROLES = "/node_roles";
+ public static final String ROLES = "/roles.json";
+
public static final String ALIASES = "/aliases.json";
/**
* This ZooKeeper file is no longer used starting with Solr 9 but keeping the name around to check if it
@@ -125,7 +134,6 @@ public class ZkStateReader implements SolrCloseable {
public static final String TLOG_REPLICAS = "tlogReplicas";
public static final String READ_ONLY = "readOnly";
- public static final String ROLES = "/roles.json";
public static final String CONFIGS_ZKNODE = "/configs";
public final static String CONFIGNAME_PROP = "configName";
[solr] 03/28: cleanup
Posted by is...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
ishan pushed a commit to branch jira/SOLR15694
in repository https://gitbox.apache.org/repos/asf/solr.git
commit d694418a62f79024147b8056272263f16293d60f
Author: Noble Paul <no...@gmail.com>
AuthorDate: Wed Nov 10 00:13:37 2021 +1100
cleanup
---
solr/core/src/java/org/apache/solr/cloud/ZkController.java | 5 -----
1 file changed, 5 deletions(-)
diff --git a/solr/core/src/java/org/apache/solr/cloud/ZkController.java b/solr/core/src/java/org/apache/solr/cloud/ZkController.java
index 8dee09e..48c26a4 100644
--- a/solr/core/src/java/org/apache/solr/cloud/ZkController.java
+++ b/solr/core/src/java/org/apache/solr/cloud/ZkController.java
@@ -46,9 +46,6 @@ import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
-import java.util.function.BiConsumer;
-import java.util.function.BiFunction;
-import java.util.function.Function;
import java.util.function.Supplier;
import com.google.common.base.Strings;
@@ -67,9 +64,7 @@ import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.common.cloud.*;
import org.apache.solr.common.cloud.Replica.Type;
-import org.apache.solr.common.params.CollectionParams;
import org.apache.solr.common.params.CommonParams;
-import org.apache.solr.common.params.CoreAdminParams;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.util.ExecutorUtil;
import org.apache.solr.common.util.IOUtils;
[solr] 10/28: refactor
Posted by is...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
ishan pushed a commit to branch jira/SOLR15694
in repository https://gitbox.apache.org/repos/asf/solr.git
commit fa8059e1dfe5fddd2bcf90dd9e465c6412d0c09b
Author: Noble Paul <no...@gmail.com>
AuthorDate: Thu Nov 18 13:53:40 2021 +1100
refactor
---
solr/core/src/java/org/apache/solr/cloud/api/collections/Assign.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/Assign.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/Assign.java
index aaa9591..5a74d78 100644
--- a/solr/core/src/java/org/apache/solr/cloud/api/collections/Assign.java
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/Assign.java
@@ -260,7 +260,7 @@ public class Assign {
if (data != null && data.getData() != null && data.getData().length > 0) {
@SuppressWarnings("unchecked")
List<String> roles = (List<String>) Utils.fromJSON(data.getData());
- if (roles.contains(NodeRole.Role.DATA.toString()) == false) {
+ if (!roles.contains(NodeRole.Role.DATA.toString())) {
liveNodes.remove(name);
}
}
[solr] 11/28: unused import
Posted by is...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
ishan pushed a commit to branch jira/SOLR15694
in repository https://gitbox.apache.org/repos/asf/solr.git
commit 0587057a94c1b5e2ffccd4c5f34881bb30d3365f
Author: Noble Paul <no...@gmail.com>
AuthorDate: Thu Nov 18 13:54:11 2021 +1100
unused import
---
solr/core/src/java/org/apache/solr/core/NodeRole.java | 1 -
1 file changed, 1 deletion(-)
diff --git a/solr/core/src/java/org/apache/solr/core/NodeRole.java b/solr/core/src/java/org/apache/solr/core/NodeRole.java
index 01c96fd..a4cf757 100644
--- a/solr/core/src/java/org/apache/solr/core/NodeRole.java
+++ b/solr/core/src/java/org/apache/solr/core/NodeRole.java
@@ -21,7 +21,6 @@ import java.lang.invoke.MethodHandles;
import java.util.*;
import org.apache.solr.common.IteratorWriter;
-import org.apache.solr.common.MapWriter;
import org.apache.solr.common.StringUtils;
import org.apache.solr.common.util.StrUtils;
import org.slf4j.Logger;
[solr] 08/28: tests were failing intermittently
Posted by is...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
ishan pushed a commit to branch jira/SOLR15694
in repository https://gitbox.apache.org/repos/asf/solr.git
commit d4d98e58b067d108da5743f6a4f3413bdb4a9197
Author: Noble Paul <no...@gmail.com>
AuthorDate: Wed Nov 17 22:12:44 2021 +1100
tests were failing intermittently
---
.../org/apache/solr/cloud/OverseerNodePrioritizer.java | 18 ++++++++++++++----
.../src/java/org/apache/solr/cloud/ZkController.java | 9 +++------
.../solr/cloud/api/collections/OverseerRoleCmd.java | 11 ++++++++++-
.../src/java/org/apache/solr/core/CoreContainer.java | 8 ++++++++
.../src/java/org/apache/solr/handler/ClusterAPI.java | 13 +++++++++----
.../src/test/org/apache/solr/cloud/NodeRolesTest.java | 17 +++++++++++++++++
6 files changed, 61 insertions(+), 15 deletions(-)
diff --git a/solr/core/src/java/org/apache/solr/cloud/OverseerNodePrioritizer.java b/solr/core/src/java/org/apache/solr/cloud/OverseerNodePrioritizer.java
index c02403e..9a423a2 100644
--- a/solr/core/src/java/org/apache/solr/cloud/OverseerNodePrioritizer.java
+++ b/solr/core/src/java/org/apache/solr/cloud/OverseerNodePrioritizer.java
@@ -17,15 +17,18 @@
package org.apache.solr.cloud;
import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import org.apache.solr.client.solrj.impl.ZkDistribStateManager;
import org.apache.solr.common.cloud.SolrZkClient;
import org.apache.solr.common.cloud.ZkStateReader;
import org.apache.solr.common.params.CoreAdminParams;
import org.apache.solr.common.params.CoreAdminParams.CoreAdminAction;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.util.Utils;
+import org.apache.solr.handler.ClusterAPI;
import org.apache.solr.handler.component.ShardHandler;
import org.apache.solr.handler.component.ShardHandlerFactory;
import org.apache.solr.handler.component.ShardRequest;
@@ -59,11 +62,18 @@ public class OverseerNodePrioritizer {
public synchronized void prioritizeOverseerNodes(String overseerId) throws Exception {
SolrZkClient zk = zkStateReader.getZkClient();
- if(!zk.exists(ZkStateReader.ROLES,true))return;
- Map<?,?> m = (Map<?,?>) Utils.fromJSON(zk.getData(ZkStateReader.ROLES, null, new Stat(), true));
+ List<String> overseerDesignates = new ArrayList<>();
+ if(zk.exists(ZkStateReader.ROLES,true)) {
+ Map<?,?> m = (Map<?,?>) Utils.fromJSON(zk.getData(ZkStateReader.ROLES, null, new Stat(), true));
+ @SuppressWarnings("unchecked")
+ List<String> l = (List<String>) m.get("overseer");
+ if(l != null) {
+ overseerDesignates.addAll(l);
+ }
+ }
- List<?> overseerDesignates = (List<?>) m.get("overseer");
- if(overseerDesignates==null || overseerDesignates.isEmpty()) return;
+ overseerDesignates.addAll(ClusterAPI.getNodesByRole("overseer", new ZkDistribStateManager(zkStateReader.getZkClient())));
+ if(overseerDesignates.isEmpty()) return;
String ldr = OverseerTaskProcessor.getLeaderNode(zk);
if(overseerDesignates.contains(ldr)) return;
log.info("prioritizing overseer nodes at {} overseer designates are {}", overseerId, overseerDesignates);
diff --git a/solr/core/src/java/org/apache/solr/cloud/ZkController.java b/solr/core/src/java/org/apache/solr/cloud/ZkController.java
index 00274aa..c955497 100644
--- a/solr/core/src/java/org/apache/solr/cloud/ZkController.java
+++ b/solr/core/src/java/org/apache/solr/cloud/ZkController.java
@@ -2198,10 +2198,6 @@ public class ZkController implements Closeable {
public void checkOverseerDesignate() {
try {
- if(cc.nodeRole.role() == NodeRole.Type.overseer) {
- setPreferredOverseer();
- return;
- }
byte[] data = zkClient.getData(ZkStateReader.ROLES, null, new Stat(), true);
if (data == null) return;
Map<?,?> roles = (Map<?,?>) Utils.fromJSON(data);
@@ -2218,10 +2214,11 @@ public class ZkController implements Closeable {
}
}
- private void setPreferredOverseer() throws KeeperException, InterruptedException {
+ public void setPreferredOverseer() throws KeeperException, InterruptedException {
ZkNodeProps props = new ZkNodeProps(Overseer.QUEUE_OPERATION, ADDROLE.toString().toLowerCase(Locale.ROOT),
"node", getNodeName(),
- "role", "overseer");
+ "role", "overseer",
+ "persist", "false");
log.info("Going to add role {} ", props);
getOverseerCollectionQueue().offer(Utils.toJSON(props));
}
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/OverseerRoleCmd.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/OverseerRoleCmd.java
index e0766e6..409c480 100644
--- a/solr/core/src/java/org/apache/solr/cloud/api/collections/OverseerRoleCmd.java
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/OverseerRoleCmd.java
@@ -64,6 +64,10 @@ public class OverseerRoleCmd implements CollApiCmds.CollectionApiCommand {
SolrZkClient zkClient = zkStateReader.getZkClient();
Map<String, List<String>> roles = null;
String node = message.getStr("node");
+ if("false".equals(message.getStr("persist"))) {// no need to persist to roles.json
+ runPrioritizer();
+ return;
+ }
String roleName = message.getStr("role");
boolean nodeExists = false;
@@ -89,16 +93,21 @@ public class OverseerRoleCmd implements CollApiCmds.CollectionApiCommand {
} else {
zkClient.create(ZkStateReader.ROLES, Utils.toJSON(roles), CreateMode.PERSISTENT, true);
}
+ runPrioritizer();
+
+ }
+
+ private void runPrioritizer() {
//if there are too many nodes this command may time out. And most likely dedicated
// overseers are created when there are too many nodes . So , do this operation in a separate thread
new Thread(() -> {
try {
+ log.info("prioritizeOverseerNodes()");
overseerPrioritizer.prioritizeOverseerNodes(ccc.getOverseerId());
} catch (Exception e) {
log.error("Error in prioritizing Overseer", e);
}
}, "OverseerPrioritizationThread").start();
-
}
}
diff --git a/solr/core/src/java/org/apache/solr/core/CoreContainer.java b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
index f85f3d7..40d5f89 100644
--- a/solr/core/src/java/org/apache/solr/core/CoreContainer.java
+++ b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
@@ -947,6 +947,14 @@ public class CoreContainer {
});
clusterSingletons.setReady();
+ if(nodeRole.role() == NodeRole.Type.overseer) {
+ try {
+ log.info("This node is started as an overseer");
+ zkSys.getZkController().setPreferredOverseer();
+ } catch (KeeperException | InterruptedException e) {
+ throw new SolrException(ErrorCode.SERVER_ERROR, e);
+ }
+ }
if (!distributedCollectionCommandRunner.isPresent()) {
zkSys.getZkController().checkOverseerDesignate();
}
diff --git a/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java b/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
index 74b221f..9541191 100644
--- a/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
+++ b/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
@@ -17,12 +17,14 @@
package org.apache.solr.handler;
+import java.io.IOException;
import java.util.*;
import com.google.common.collect.Maps;
import org.apache.solr.api.Command;
import org.apache.solr.api.EndPoint;
import org.apache.solr.api.PayloadObj;
+import org.apache.solr.client.solrj.cloud.DistribStateManager;
import org.apache.solr.client.solrj.request.beans.ClusterPropPayload;
import org.apache.solr.client.solrj.request.beans.CreateConfigPayload;
import org.apache.solr.client.solrj.request.beans.RateLimiterPayload;
@@ -43,6 +45,7 @@ import org.apache.solr.handler.admin.CollectionsHandler;
import org.apache.solr.handler.admin.ConfigSetsHandler;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;
+import org.apache.zookeeper.KeeperException;
import static org.apache.solr.client.solrj.SolrRequest.METHOD.DELETE;
import static org.apache.solr.client.solrj.SolrRequest.METHOD.GET;
@@ -92,11 +95,13 @@ public class ClusterAPI {
permission = COLL_READ_PERM)
public void nodesWithRole(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
String role = req.getPathTemplateValues().get("role");
+ rsp.add("nodes",
+ getNodesByRole(role, collectionsHandler.getCoreContainer().getZkController().getSolrCloudManager().getDistribStateManager()));
+ }
+ public static List<String> getNodesByRole(String role, DistribStateManager zk) throws InterruptedException, IOException, KeeperException {
List<String> result = new ArrayList<>();
- collectionsHandler.getCoreContainer().getZkController().getSolrCloudManager().getDistribStateManager().
- forEachChild(ZkStateReader.NODE_ROLES, (node, data) -> {
-
+ zk.forEachChild(ZkStateReader.NODE_ROLES, (node, data) -> {
if(data != null && data.getData() != null) {
@SuppressWarnings("unchecked")
Map<String, Object> roleData = (Map<String, Object>) Utils.fromJSON(data.getData());
@@ -105,7 +110,7 @@ public class ClusterAPI {
}
}
});
- rsp.add("nodes", result);
+ return result;
}
diff --git a/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java b/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java
index 70ab96b..42c5a0f 100644
--- a/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java
@@ -1,3 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.
+ */
+
package org.apache.solr.cloud;
import java.lang.invoke.MethodHandles;
[solr] 15/28: SOLR-15694: Ref guide changes
Posted by is...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
ishan pushed a commit to branch jira/SOLR15694
in repository https://gitbox.apache.org/repos/asf/solr.git
commit e4e24dce8aa7540a1d1e0287ddac071ac468317d
Author: Ishan Chattopadhyaya <is...@apache.org>
AuthorDate: Fri Dec 17 14:15:01 2021 +0530
SOLR-15694: Ref guide changes
---
solr/solr-ref-guide/src/node-roles.adoc | 65 +++++++++++++++++++++++++
solr/solr-ref-guide/src/solrcloud-clusters.adoc | 4 ++
2 files changed, 69 insertions(+)
diff --git a/solr/solr-ref-guide/src/node-roles.adoc b/solr/solr-ref-guide/src/node-roles.adoc
new file mode 100644
index 0000000..f3e5969
--- /dev/null
+++ b/solr/solr-ref-guide/src/node-roles.adoc
@@ -0,0 +1,65 @@
+= Node Roles
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF 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.
+
+A node in Solr is usually capable of performing various types of operations, e.g. hosting replicas, performing indexing and querying on them, collection management tasks etc. However, if someone wants to setup a cluster where these functionalities are isolated to certain dedicated nodes, then this can be achieved leveraging the concept of node roles.
+
+== Definitions
+
+=== Node role
+
+A role is a designation of a node that indicates that the node may perform a certain functionality that is governed by the role.
+
+=== Mode
+Every role has a list of modes under which a node can be. It can be simple (e.g. `["on", "off"]`) or more granular (e.g. `["allowed", "preferred", "disallowed"]`).
+
+== Roles
+
+In order to specify role(s) for a node, one needs to start a Solr node with the following parameter.
+
+.Startup Parameter
+[cols="1,2,1,1"]
+|===
+|Parameter |Value |Required? | Default
+
+|solr.node.roles
+|Comma separated list of roles (in the format: <role>:<mode>) for this node.
+Examples: "data:on,overseer:allowed" or "overseer:preferred"
+|No
+|data:on,overseer:allowed
+|===
+
+[TIP]
+====
+If a node has been started with no `solr.node.roles` parameter, it will be assumed to have the data role turned on and overseer role allowed on it. If you've never needed roles before, you likely won't need to change anything in your startup parameters to accomodate the functionality associated with these roles.
+====
+
+
+=== `data` role
+A node with this role (in mode "on") can host shards and replicas for collections.
+
+=== `overseer` role
+A node with this role can perform duties of an overseer node (unless mode is `disallowed`). When one or more nodes have the overseer role in `preferred` mode, the overseer leader will be elected from one of these nodes. In case no node is designated as a preferred overseer or no such node is live, the overseer leader will be elected from one of the nodes that have the overseer role in `allowed` mode. If all nodes that are designated with overseer role (allowed or preferred) are down, the [...]
+
+== A typical scenario of roles usage
+
+A common situation arises when the nodes in a cluster is under heavy querying or indexing load, and under such circumstances the overseer leader node is unable to perform collection management duties efficiently. It might be reasonable to have dedicated nodes to act as the overseer. Such an effect can be achieved as follows:
+
+* Most nodes, say 10 of them, in the cluster start with `-Dsolr.node.roles=data:on,overseer:allowed` (or with no parameter, since the default value for `solr.node.roles` is the same).
+* One or more nodes can start with `-Dsolr.node.roles=overseer:preferred` (or `-Dsolr.node.roles=overseer:preferred,data:off`)
+
+In this arrangement, such dedicated nodes can have lesser resources than other data nodes (since these are stateless nodes) and yet the cluster will behave optimally. In case the dedicated overseer nodes go down for some reason, the overseer leader will be elected from one of the data nodes (since they have overseer in "allowed" mode), and once one of the dedicated overseer nodes are back up, it will assume the overseer leadership again.
\ No newline at end of file
diff --git a/solr/solr-ref-guide/src/solrcloud-clusters.adoc b/solr/solr-ref-guide/src/solrcloud-clusters.adoc
index 83b3fde..03723f5 100644
--- a/solr/solr-ref-guide/src/solrcloud-clusters.adoc
+++ b/solr/solr-ref-guide/src/solrcloud-clusters.adoc
@@ -3,6 +3,7 @@
solrcloud-recoveries-and-write-tolerance, \
solrcloud-distributed-requests, \
aliases, \
+ node-roles, \
cluster-node-management, \
shard-management, \
replica-management, \
@@ -47,6 +48,9 @@ ZooKeeper is a critical component of SolrCloud, used to manage shard and replica
| <<solrcloud-recoveries-and-write-tolerance.adoc#,SolrCloud Recoveries and Write Tolerance>>: Recovery in a SolrCloud cluster.
| <<solrcloud-distributed-requests.adoc#,SolrCloud Distributed Requests>>: Query routing in a SolrCloud cluster.
| <<aliases.adoc#,Aliases>>: Alternative names for collections.
+2+^h| Cluster Management
+| <<node-roles.adoc#,Node Roles>>: Functional roles for nodes for multi-tiered clusters.
+|
2+^h| Collections API
|<<cluster-node-management.adoc#,Cluster and Node Management>>: Cluster management commands of the Collections API.
| <<shard-management.adoc#,Shard Management>>: Shard management commands of the Collections API.
[solr] 23/28: Ref guide fixes
Posted by is...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
ishan pushed a commit to branch jira/SOLR15694
in repository https://gitbox.apache.org/repos/asf/solr.git
commit a501b777c64d126aaeac92c22d858a6f281ed9be
Author: Ishan Chattopadhyaya <is...@apache.org>
AuthorDate: Sat Dec 18 15:40:38 2021 +0530
Ref guide fixes
---
solr/solr-ref-guide/src/node-roles.adoc | 29 ++++++++++++++++++++++++-----
1 file changed, 24 insertions(+), 5 deletions(-)
diff --git a/solr/solr-ref-guide/src/node-roles.adoc b/solr/solr-ref-guide/src/node-roles.adoc
index ed2c07a..19c5beb 100644
--- a/solr/solr-ref-guide/src/node-roles.adoc
+++ b/solr/solr-ref-guide/src/node-roles.adoc
@@ -37,10 +37,10 @@ In order to specify role(s) for a node, one needs to start a Solr node with the
|Parameter |Value |Required? | Default
|solr.node.roles
-|Comma separated list of roles (in the format: <role>:<mode>) for this node.
-Examples: "data:on,overseer:allowed" or "overseer:preferred"
+|Comma separated list of roles (in the format: `<role>:<mode>`) for this node.
+Examples: `-Dsolr.node.roles=data:on,overseer:allowed` or `-Dsolr.node.roles=overseer:preferred`
|No
-|data:on,overseer:allowed
+|`data:on,overseer:allowed`
|===
[TIP]
@@ -48,6 +48,17 @@ Examples: "data:on,overseer:allowed" or "overseer:preferred"
If a node has been started with no `solr.node.roles` parameter, it will be assumed to have the data role turned on and overseer role allowed on it. If you've never needed roles before, you likely won't need to change anything in your startup parameters to accomodate the functionality associated with these roles.
====
+.Supported roles
+[cols="1,1"]
+|===
+|Role |Modes
+
+|`data`
+|on, off
+
+|`overseer`
+|allowed, preferred, disallowed
+|===
=== `data` role
A node with this role (in mode "on") can host shards and replicas for collections.
@@ -55,9 +66,9 @@ A node with this role (in mode "on") can host shards and replicas for collection
=== `overseer` role
A node with this role can perform duties of an overseer node (unless mode is `disallowed`). When one or more nodes have the overseer role in `preferred` mode, the overseer leader will be elected from one of these nodes. In case no node is designated as a preferred overseer or no such node is live, the overseer leader will be elected from one of the nodes that have the overseer role in `allowed` mode. If all nodes that are designated with overseer role (allowed or preferred) are down, the [...]
-== A typical scenario of roles usage
+== Example usage
-A common situation arises when the nodes in a cluster is under heavy querying or indexing load, and under such circumstances the overseer leader node is unable to perform collection management duties efficiently. It might be reasonable to have dedicated nodes to act as the overseer. Such an effect can be achieved as follows:
+Sometimes, when the nodes in a cluster is under heavy querying or indexing load, the overseer leader node is unable to perform collection management duties efficiently. It might be reasonable to have dedicated nodes to act as the overseer. Such an effect can be achieved as follows:
* Most nodes, say 10 of them, in the cluster start with `-Dsolr.node.roles=data:on,overseer:allowed` (or with no parameter, since the default value for `solr.node.roles` is the same).
* One or more nodes can start with `-Dsolr.node.roles=overseer:preferred` (or `-Dsolr.node.roles=overseer:preferred,data:off`)
@@ -68,6 +79,8 @@ In this arrangement, such dedicated nodes can have lesser resources than other d
=== GET /api/cluster/node-roles/supported
+Fetches the list of supported roles and their supported modes for this cluster.
+
*Input*
[source,text]
----
@@ -96,6 +109,8 @@ curl http://localhost:8983/api/cluster/node-roles/supported
=== GET /api/cluster/node-roles
+Fetches the current node roles assignment for all the nodes in the cluster.
+
*Input*
[source,text]
----
@@ -122,6 +137,8 @@ curl http://localhost:8983/api/cluster/node-roles
=== GET /api/cluster/node-roles/{role}
+Fetches the current node roles assignment for a specified role.
+
*Input*
[source,text]
----
@@ -144,6 +161,8 @@ http://localhost:8983/api/node-roles/data
=== GET /api/cluster/node-roles/nodes/{node}
+Fetches the current node roles assignment for the specified node.
+
*Input*
[source,text]
----
[solr] 19/28: removed unnecessary changes
Posted by is...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
ishan pushed a commit to branch jira/SOLR15694
in repository https://gitbox.apache.org/repos/asf/solr.git
commit 1cf58d7acdb16463f764901ec83c75dfb84056ca
Author: Noble Paul <no...@gmail.com>
AuthorDate: Fri Dec 17 16:40:09 2021 +1100
removed unnecessary changes
---
.../src/java/org/apache/solr/cloud/api/collections/OverseerRoleCmd.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/OverseerRoleCmd.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/OverseerRoleCmd.java
index 35c4bd6..191e33b 100644
--- a/solr/core/src/java/org/apache/solr/cloud/api/collections/OverseerRoleCmd.java
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/OverseerRoleCmd.java
@@ -102,12 +102,12 @@ public class OverseerRoleCmd implements CollApiCmds.CollectionApiCommand {
// overseers are created when there are too many nodes . So , do this operation in a separate thread
new Thread(() -> {
try {
- log.info("prioritizeOverseerNodes()");
overseerPrioritizer.prioritizeOverseerNodes(ccc.getOverseerId());
} catch (Exception e) {
log.error("Error in prioritizing Overseer", e);
}
}, "OverseerPrioritizationThread").start();
+
}
}
[solr] 09/28: Multiple roles supported per node
Posted by is...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
ishan pushed a commit to branch jira/SOLR15694
in repository https://gitbox.apache.org/repos/asf/solr.git
commit 84982ef4308451a4ff8096b6f479aa1e62603f11
Author: Ishan Chattopadhyaya <is...@apache.org>
AuthorDate: Wed Nov 17 19:14:34 2021 +0530
Multiple roles supported per node
---
.../apache/solr/cloud/OverseerNodePrioritizer.java | 6 +-
.../java/org/apache/solr/cloud/ZkController.java | 9 ++-
.../apache/solr/cloud/api/collections/Assign.java | 9 ++-
.../cloud/api/collections/OverseerRoleCmd.java | 2 +-
.../java/org/apache/solr/core/CoreContainer.java | 4 +-
.../src/java/org/apache/solr/core/NodeRole.java | 69 +++++++++++---------
.../java/org/apache/solr/handler/ClusterAPI.java | 17 +++--
.../test/org/apache/solr/cloud/NodeRolesTest.java | 73 +++++++++-------------
.../client/solrj/cloud/DistribStateManager.java | 12 ++--
9 files changed, 98 insertions(+), 103 deletions(-)
diff --git a/solr/core/src/java/org/apache/solr/cloud/OverseerNodePrioritizer.java b/solr/core/src/java/org/apache/solr/cloud/OverseerNodePrioritizer.java
index 9a423a2..f88c686 100644
--- a/solr/core/src/java/org/apache/solr/cloud/OverseerNodePrioritizer.java
+++ b/solr/core/src/java/org/apache/solr/cloud/OverseerNodePrioritizer.java
@@ -63,17 +63,17 @@ public class OverseerNodePrioritizer {
public synchronized void prioritizeOverseerNodes(String overseerId) throws Exception {
SolrZkClient zk = zkStateReader.getZkClient();
List<String> overseerDesignates = new ArrayList<>();
- if(zk.exists(ZkStateReader.ROLES,true)) {
+ if (zk.exists(ZkStateReader.ROLES,true)) {
Map<?,?> m = (Map<?,?>) Utils.fromJSON(zk.getData(ZkStateReader.ROLES, null, new Stat(), true));
@SuppressWarnings("unchecked")
List<String> l = (List<String>) m.get("overseer");
- if(l != null) {
+ if (l != null) {
overseerDesignates.addAll(l);
}
}
overseerDesignates.addAll(ClusterAPI.getNodesByRole("overseer", new ZkDistribStateManager(zkStateReader.getZkClient())));
- if(overseerDesignates.isEmpty()) return;
+ if (overseerDesignates.isEmpty()) return;
String ldr = OverseerTaskProcessor.getLeaderNode(zk);
if(overseerDesignates.contains(ldr)) return;
log.info("prioritizing overseer nodes at {} overseer designates are {}", overseerId, overseerDesignates);
diff --git a/solr/core/src/java/org/apache/solr/cloud/ZkController.java b/solr/core/src/java/org/apache/solr/cloud/ZkController.java
index c955497..05c6c5b 100644
--- a/solr/core/src/java/org/apache/solr/cloud/ZkController.java
+++ b/solr/core/src/java/org/apache/solr/cloud/ZkController.java
@@ -79,7 +79,6 @@ import org.apache.solr.core.CoreContainer;
import org.apache.solr.core.CoreDescriptor;
import org.apache.solr.core.SolrCore;
import org.apache.solr.core.SolrCoreInitializationException;
-import org.apache.solr.core.NodeRole;
import org.apache.solr.handler.component.HttpShardHandler;
import org.apache.solr.logging.MDCLoggingContext;
import org.apache.solr.search.SolrIndexSearcher;
@@ -1088,11 +1087,11 @@ public class ZkController implements Closeable {
log.info("Register node as live in ZooKeeper:{}", nodePath);
List<Op> ops = new ArrayList<>(2);
ops.add(Op.create(nodePath, null, zkClient.getZkACLProvider().getACLsToAdd(nodePath), CreateMode.EPHEMERAL));
- if(cc.nodeRole.role() != NodeRole.Type.data) {
- //this is a non-data node
- ops.add(Op.create(ZkStateReader.NODE_ROLES + "/" + nodeName, Utils.toJSON(cc.nodeRole), zkClient.getZkACLProvider().getACLsToAdd(nodePath), CreateMode.EPHEMERAL));
- }
+ // Create the roles node as well
+ ops.add(Op.create(ZkStateReader.NODE_ROLES + "/" + nodeName,
+ Utils.toJSON(cc.nodeRoles), zkClient.getZkACLProvider().getACLsToAdd(nodePath), CreateMode.EPHEMERAL));
+
zkClient.multi(ops, true);
}
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/Assign.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/Assign.java
index 5f726c9..aaa9591 100644
--- a/solr/core/src/java/org/apache/solr/cloud/api/collections/Assign.java
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/Assign.java
@@ -257,17 +257,16 @@ public class Assign {
public static void filterNonDataNodes(DistribStateManager zk, List<String> liveNodes) {
try {
zk.forEachChild(ZkStateReader.NODE_ROLES, (name, data) -> {
- if(data != null && data.getData() != null && data.getData().length >0) {
+ if (data != null && data.getData() != null && data.getData().length > 0) {
@SuppressWarnings("unchecked")
- Map<String,Object> map = (Map<String, Object>) Utils.fromJSON(data.getData());
- if(Boolean.FALSE.equals(map.get(NodeRole.HAS_DATA))) {
+ List<String> roles = (List<String>) Utils.fromJSON(data.getData());
+ if (roles.contains(NodeRole.Role.DATA.toString()) == false) {
liveNodes.remove(name);
}
}
});
-
} catch (Exception e) {
- throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unable to communicate with Zookeeper");
+ throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Problem fetching roles from Zookeeper", e);
}
}
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/OverseerRoleCmd.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/OverseerRoleCmd.java
index 409c480..35c4bd6 100644
--- a/solr/core/src/java/org/apache/solr/cloud/api/collections/OverseerRoleCmd.java
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/OverseerRoleCmd.java
@@ -64,7 +64,7 @@ public class OverseerRoleCmd implements CollApiCmds.CollectionApiCommand {
SolrZkClient zkClient = zkStateReader.getZkClient();
Map<String, List<String>> roles = null;
String node = message.getStr("node");
- if("false".equals(message.getStr("persist"))) {// no need to persist to roles.json
+ if ("false".equals(message.getStr("persist"))) {// no need to persist to roles.json
runPrioritizer();
return;
}
diff --git a/solr/core/src/java/org/apache/solr/core/CoreContainer.java b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
index 40d5f89..44c63db 100644
--- a/solr/core/src/java/org/apache/solr/core/CoreContainer.java
+++ b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
@@ -246,7 +246,7 @@ public class CoreContainer {
private final ObjectCache objectCache = new ObjectCache();
- public final NodeRole nodeRole = new NodeRole(System.getProperty(NodeRole.NODE_ROLE));
+ public final NodeRole nodeRoles = new NodeRole(System.getProperty(NodeRole.NODE_ROLES_PROP));
private final ClusterSingletons clusterSingletons = new ClusterSingletons(
() -> getZkController() != null &&
@@ -947,7 +947,7 @@ public class CoreContainer {
});
clusterSingletons.setReady();
- if(nodeRole.role() == NodeRole.Type.overseer) {
+ if (nodeRoles.getRoles().contains(NodeRole.Role.OVERSEER)) {
try {
log.info("This node is started as an overseer");
zkSys.getZkController().setPreferredOverseer();
diff --git a/solr/core/src/java/org/apache/solr/core/NodeRole.java b/solr/core/src/java/org/apache/solr/core/NodeRole.java
index f0dd794..01c96fd 100644
--- a/solr/core/src/java/org/apache/solr/core/NodeRole.java
+++ b/solr/core/src/java/org/apache/solr/core/NodeRole.java
@@ -17,53 +17,62 @@
package org.apache.solr.core;
import java.io.IOException;
+import java.lang.invoke.MethodHandles;
import java.util.*;
+
+import org.apache.solr.common.IteratorWriter;
import org.apache.solr.common.MapWriter;
import org.apache.solr.common.StringUtils;
import org.apache.solr.common.util.StrUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class NodeRole implements IteratorWriter {
+ private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ public static final String NODE_ROLES_PROP = "solr.node.roles";
-public class NodeRole implements MapWriter {
- private boolean hasData = true;
- private Type role =Type.data;
+ private final Set<Role> roles;
public NodeRole(String role) {
if (StringUtils.isEmpty(role)) {
+ // if no roles were specified, assume "data" role for backcompat reasons
+ roles = Set.of(Role.DATA);
return;
}
- Set<String> roles = new HashSet<>(StrUtils.split(role, ','));
- if (roles.isEmpty()) return;
- for (String s : roles) {
- if (Type.data.name().equals(s)) {
- hasData = true;
- continue;
- }
- if (Type.overseer.name().equals(s)) {
- this.role = Type.overseer;
- hasData = false;
- } else {
- throw new RuntimeException("Unknown type");
- }
+ Set<String> rolesSet = new HashSet<>(StrUtils.split(role, ','));
+ if (rolesSet.isEmpty()) {
+ // if no roles were specified, assume "data" role for backcompat reasons
+ roles = Set.of(Role.DATA);
+ return;
+ }
+ roles = new TreeSet<>();
+ for (String r: rolesSet) {
+ roles.add(Role.valueOfCaseInsensitive(r));
}
-
- }
-
- public boolean hasData() {
- return hasData;
}
- public Type role() {
- return role;
+ public Set<Role> getRoles() {
+ return roles;
}
@Override
- public void writeMap(EntryWriter ew) throws IOException {
- ew.put(HAS_DATA, hasData);
- ew.put("role", role.name());
+ public void writeIter(ItemWriter iw) throws IOException {
+ for (Role role: roles) iw.add(role.toString());
}
- public enum Type {
- data, overseer;
+ public enum Role {
+ DATA, OVERSEER;
+
+ public static Role valueOfCaseInsensitive(String value) {
+ // Given a user string "overseer", convert to OVERSEER and return the enum value
+ String canonicalValue = value.toUpperCase().replace(' ', '_');
+ return Role.valueOf(canonicalValue);
+ }
+
+ @Override
+ public String toString() {
+ return super.toString().toLowerCase();
+ }
}
- public static final String HAS_DATA = "hasData";
- public static final String NODE_ROLE = "solr.node.role";
}
diff --git a/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java b/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
index 9541191..5f512d2 100644
--- a/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
+++ b/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
@@ -18,6 +18,7 @@
package org.apache.solr.handler;
import java.io.IOException;
+import java.lang.invoke.MethodHandles;
import java.util.*;
import com.google.common.collect.Maps;
@@ -46,6 +47,8 @@ import org.apache.solr.handler.admin.ConfigSetsHandler;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.zookeeper.KeeperException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import static org.apache.solr.client.solrj.SolrRequest.METHOD.DELETE;
import static org.apache.solr.client.solrj.SolrRequest.METHOD.GET;
@@ -74,17 +77,19 @@ public class ClusterAPI {
this.collectionsHandler = ch;
this.configSetsHandler = configSetsHandler;
}
+ private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@EndPoint(method = GET,
path = "/cluster/node-roles",
permission = COLL_READ_PERM)
@SuppressWarnings("unchecked")
+ // nocommit: it must also output all data nodes that didn't start with -Dsolr.node.roles
public void roles(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
- Map <String, Map<String,Object>> result = new LinkedHashMap<>();
+ Map <String, List<String>> result = new LinkedHashMap<>();
collectionsHandler.getCoreContainer().getZkController().getSolrCloudManager().getDistribStateManager().
forEachChild(ZkStateReader.NODE_ROLES, (node, data) -> {
- if(data != null && data.getData() != null) {
- result.put(node, (Map<String, Object>) Utils.fromJSON(data.getData()));
+ if (data != null && data.getData() != null) {
+ result.put(node, ((List<String>) Utils.fromJSON(data.getData())));
}
});
rsp.add("node-roles", result);
@@ -102,10 +107,10 @@ public class ClusterAPI {
public static List<String> getNodesByRole(String role, DistribStateManager zk) throws InterruptedException, IOException, KeeperException {
List<String> result = new ArrayList<>();
zk.forEachChild(ZkStateReader.NODE_ROLES, (node, data) -> {
- if(data != null && data.getData() != null) {
+ if (data != null && data.getData() != null) {
@SuppressWarnings("unchecked")
- Map<String, Object> roleData = (Map<String, Object>) Utils.fromJSON(data.getData());
- if(role.equals(roleData.get("role"))) {
+ List<String> rolesData = (List<String>) Utils.fromJSON(data.getData());
+ if (rolesData.contains(role)) {
result.add(node);
}
}
diff --git a/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java b/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java
index 42c5a0f..e5f2ee1 100644
--- a/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java
@@ -19,6 +19,8 @@ package org.apache.solr.cloud;
import java.lang.invoke.MethodHandles;
import java.util.List;
+import java.util.TreeSet;
+
import org.apache.solr.client.solrj.embedded.JettySolrRunner;
import org.apache.solr.client.solrj.request.CollectionAdminRequest;
import org.apache.solr.client.solrj.request.V2Request;
@@ -49,47 +51,20 @@ public class NodeRolesTest extends SolrCloudTestCase {
JettySolrRunner j0 = cluster.getJettySolrRunner(0);
JettySolrRunner j1 = null, j2 = null;
- System.setProperty(NodeRole.NODE_ROLE, "overseer");
- try {
- j1 = cluster.startJettySolrRunner();
- } finally {
- System.clearProperty(NodeRole.NODE_ROLE);
- }
+ j1 = startNodeWithRoles("overseer");
- V2Response rsp = new V2Request
- .Builder("/cluster/node-roles")
- .GET()
- .build()
- .process(cluster.getSolrClient());
- assertEquals(Boolean.FALSE, rsp._get(List.of("node-roles", j1.getNodeName(), NodeRole.HAS_DATA), null));
- assertEquals("overseer", rsp._get(List.of("node-roles", j1.getNodeName(), "role"), null));
+ V2Response rsp = new V2Request.Builder("/cluster/node-roles").GET().build().process(cluster.getSolrClient());
+ assertEquals(List.of("overseer"), rsp._get(List.of("node-roles", j1.getNodeName()), null));
OverseerRolesTest.waitForNewOverseer(20, j1.getNodeName(), false);
- //start another node that is overseer but has data
- System.setProperty(NodeRole.NODE_ROLE, "overseer,data");
- try {
- j2 = cluster.startJettySolrRunner();
- } finally {
- System.clearProperty(NodeRole.NODE_ROLE);
- }
- rsp = new V2Request
- .Builder("/cluster/node-roles")
- .GET()
- .build()
- .process(cluster.getSolrClient());
- assertEquals(Boolean.TRUE, rsp._get(List.of("node-roles", j2.getNodeName(), NodeRole.HAS_DATA), null));
- assertEquals("overseer", rsp._get(List.of("node-roles", j2.getNodeName(), "role"), null));
-
- rsp = new V2Request
- .Builder("/cluster/node-roles/overseer")
- .GET()
- .build()
- .process(cluster.getSolrClient());
- List<String> overseerNodes = (List<String>) rsp._get("nodes", null);
- assertTrue(overseerNodes.contains(j1.getNodeName()));
- assertTrue(overseerNodes.contains(j2.getNodeName()));
+ //start another node that is overseer but has data
+ j2 = startNodeWithRoles("overseer,data");
+ rsp = new V2Request.Builder("/cluster/node-roles").GET().build().process(cluster.getSolrClient());
+ assertEquals(List.of("data", "overseer"), rsp._get(List.of("node-roles", j2.getNodeName()), null));
+ rsp = new V2Request.Builder("/cluster/node-roles/overseer").GET().build().process(cluster.getSolrClient());
+ assertEquals(new TreeSet<String>(List.of(j1.getNodeName(), j2.getNodeName())), new TreeSet<String>((List<String>) rsp._get("nodes", null)));
String COLLECTION_NAME = "TEST_ROLES";
CollectionAdminRequest
@@ -97,18 +72,26 @@ public class NodeRolesTest extends SolrCloudTestCase {
.process(cluster.getSolrClient());
cluster.waitForActiveCollection(COLLECTION_NAME, 3, 3);
- String overseer = j1.getNodeName();
+ // Assert that no replica was placed on the dedicated overseer node
+ String dedicatedOverseer = j1.getNodeName();
cluster.getSolrClient().getClusterStateProvider().getCollection(COLLECTION_NAME)
- .forEachReplica((s, replica) -> assertNotEquals(replica.node, overseer));
+ .forEachReplica((s, replica) -> assertNotEquals(replica.node, dedicatedOverseer));
- //now shutdown the overseer
+ // Shutdown the dedicated overseer, make sure that node disappears from the roles output
j1.stop();
- rsp = new V2Request
- .Builder("/cluster/node-roles")
- .GET()
- .build()
- .process(cluster.getSolrClient());
- assertNull(rsp._get(List.of("node-roles", overseer), null));
+ rsp = new V2Request.Builder("/cluster/node-roles").GET().build().process(cluster.getSolrClient());
+ assertNull(rsp._get(List.of("node-roles", dedicatedOverseer), null));
+ }
+
+ private JettySolrRunner startNodeWithRoles(String roles) throws Exception {
+ JettySolrRunner jetty;
+ System.setProperty(NodeRole.NODE_ROLES_PROP, roles);
+ try {
+ jetty = cluster.startJettySolrRunner();
+ } finally {
+ System.clearProperty(NodeRole.NODE_ROLES_PROP);
+ }
+ return jetty;
}
}
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/DistribStateManager.java b/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/DistribStateManager.java
index ebd4db7..84010c8 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/DistribStateManager.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/DistribStateManager.java
@@ -143,21 +143,21 @@ public interface DistribStateManager extends SolrCloseable {
}
}
}
- default void forEachChild(String path, BiConsumer<String, VersionedData> fun)
+ default void forEachChild(String path, BiConsumer<String, VersionedData> fun)
throws InterruptedException, IOException, KeeperException {
List<String> children = null;
try {
- children = listData(ZkStateReader.NODE_ROLES);
+ children = listData(path);
} catch (NoSuchElementException e) {
return;
}
- if(!children.isEmpty()) {
- for (String node : children) {
+ if (!children.isEmpty()) {
+ for (String node: children) {
VersionedData data = null;
try {
- data = getData(ZkStateReader.NODE_ROLES + "/" + node);
+ data = getData(path + "/" + node);
} catch (NoSuchElementException e) {
- //this node probably was deleted
+ // this node must've been deleted
continue;
}
fun.accept(node, data);
[solr] 07/28: added test
Posted by is...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
ishan pushed a commit to branch jira/SOLR15694
in repository https://gitbox.apache.org/repos/asf/solr.git
commit 71c11f9abf8a06dda53deff079a8cddb1c1e955d
Author: Noble Paul <no...@gmail.com>
AuthorDate: Wed Nov 17 16:55:19 2021 +1100
added test
---
.../apache/solr/cloud/api/collections/Assign.java | 24 +++++-
.../cloud/api/collections/CreateCollectionCmd.java | 2 +-
.../solr/cloud/api/collections/RestoreCmd.java | 2 +-
.../impl/SimpleClusterAbstractionsImpl.java | 34 +-------
.../java/org/apache/solr/core/CoreContainer.java | 2 +-
.../src/java/org/apache/solr/core/NodeRole.java | 15 ++--
.../java/org/apache/solr/handler/ClusterAPI.java | 3 +-
.../test/org/apache/solr/cloud/NodeRolesTest.java | 97 ++++++++++++++++++++++
.../org/apache/solr/cloud/OverseerRolesTest.java | 4 +-
9 files changed, 133 insertions(+), 50 deletions(-)
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/Assign.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/Assign.java
index c6702ef..5f726c9 100644
--- a/solr/core/src/java/org/apache/solr/cloud/api/collections/Assign.java
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/Assign.java
@@ -51,7 +51,9 @@ import org.apache.solr.common.cloud.Slice;
import org.apache.solr.common.cloud.ZkNodeProps;
import org.apache.solr.common.cloud.ZkStateReader;
import org.apache.solr.common.util.StrUtils;
+import org.apache.solr.common.util.Utils;
import org.apache.solr.core.CoreContainer;
+import org.apache.solr.core.NodeRole;
import org.apache.solr.util.NumberUtils;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
@@ -227,7 +229,9 @@ public class Assign {
return false;
}
- public static List<String> getLiveOrLiveAndCreateNodeSetList(final Set<String> liveNodes, final ZkNodeProps message, final Random random) {
+ public static List<String> getLiveOrLiveAndCreateNodeSetList(final Set<String> liveNodes, final ZkNodeProps message, final Random random,
+ DistribStateManager zk) {
+
List<String> nodeList;
final String createNodeSetStr = message.getStr(CREATE_NODE_SET);
final List<String> createNodeList = (createNodeSetStr == null) ? null :
@@ -243,12 +247,30 @@ public class Assign {
}
} else {
nodeList = new ArrayList<>(liveNodes);
+ filterNonDataNodes(zk, nodeList);
Collections.shuffle(nodeList, random);
}
return nodeList;
}
+ public static void filterNonDataNodes(DistribStateManager zk, List<String> liveNodes) {
+ try {
+ zk.forEachChild(ZkStateReader.NODE_ROLES, (name, data) -> {
+ if(data != null && data.getData() != null && data.getData().length >0) {
+ @SuppressWarnings("unchecked")
+ Map<String,Object> map = (Map<String, Object>) Utils.fromJSON(data.getData());
+ if(Boolean.FALSE.equals(map.get(NodeRole.HAS_DATA))) {
+ liveNodes.remove(name);
+ }
+ }
+ });
+
+ } catch (Exception e) {
+ throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unable to communicate with Zookeeper");
+ }
+ }
+
static class ReplicaCount {
public final String nodeName;
public int thisCollectionNodes = 0;
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/CreateCollectionCmd.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/CreateCollectionCmd.java
index 66c21b6..e8a185e 100644
--- a/solr/core/src/java/org/apache/solr/cloud/api/collections/CreateCollectionCmd.java
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/CreateCollectionCmd.java
@@ -406,7 +406,7 @@ public class CreateCollectionCmd implements CollApiCmds.CollectionApiCommand {
// but (for now) require that each core goes on a distinct node.
List<ReplicaPosition> replicaPositions;
- List<String> nodeList = Assign.getLiveOrLiveAndCreateNodeSetList(clusterState.getLiveNodes(), message, CollectionHandlingUtils.RANDOM);
+ List<String> nodeList = Assign.getLiveOrLiveAndCreateNodeSetList(clusterState.getLiveNodes(), message, CollectionHandlingUtils.RANDOM, cloudManager.getDistribStateManager());
if (nodeList.isEmpty()) {
log.warn("It is unusual to create a collection ({}) without cores.", collectionName);
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/RestoreCmd.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/RestoreCmd.java
index 2732cb9..af247de 100644
--- a/solr/core/src/java/org/apache/solr/cloud/api/collections/RestoreCmd.java
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/RestoreCmd.java
@@ -182,7 +182,7 @@ public class RestoreCmd implements CollApiCmds.CollectionApiCommand {
this.shardHandler = ccc.newShardHandler();
this.nodeList = Assign.getLiveOrLiveAndCreateNodeSetList(
- zkStateReader.getClusterState().getLiveNodes(), message, CollectionHandlingUtils.RANDOM);
+ zkStateReader.getClusterState().getLiveNodes(), message, CollectionHandlingUtils.RANDOM, container.getZkController().getSolrCloudManager().getDistribStateManager());
}
@Override
diff --git a/solr/core/src/java/org/apache/solr/cluster/placement/impl/SimpleClusterAbstractionsImpl.java b/solr/core/src/java/org/apache/solr/cluster/placement/impl/SimpleClusterAbstractionsImpl.java
index 16ce53c..48f3f50 100644
--- a/solr/core/src/java/org/apache/solr/cluster/placement/impl/SimpleClusterAbstractionsImpl.java
+++ b/solr/core/src/java/org/apache/solr/cluster/placement/impl/SimpleClusterAbstractionsImpl.java
@@ -19,23 +19,16 @@ package org.apache.solr.cluster.placement.impl;
import java.io.IOException;
import java.util.*;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.function.BiConsumer;
import java.util.stream.Collectors;
import com.google.common.collect.Maps;
import org.apache.solr.client.solrj.cloud.SolrCloudManager;
-import org.apache.solr.client.solrj.cloud.VersionedData;
import org.apache.solr.cluster.*;
-import org.apache.solr.common.SolrException;
import org.apache.solr.common.cloud.ClusterState;
import org.apache.solr.common.cloud.DocCollection;
import org.apache.solr.common.cloud.Slice;
-import org.apache.solr.common.cloud.ZkStateReader;
import org.apache.solr.common.params.CollectionAdminParams;
import org.apache.solr.common.util.Pair;
-import org.apache.solr.common.util.Utils;
-import org.apache.zookeeper.KeeperException;
import javax.annotation.Nonnull;
@@ -63,8 +56,7 @@ class SimpleClusterAbstractionsImpl {
private final ClusterState clusterState;
ClusterImpl(SolrCloudManager solrCloudManager) throws IOException {
- Set<String> liveNodes = filterNonDataNodes(solrCloudManager);
- this.liveNodes = NodeImpl.getNodes(liveNodes);
+ liveNodes = NodeImpl.getNodes(solrCloudManager.getClusterStateProvider().getLiveNodes());
clusterState = solrCloudManager.getClusterStateProvider().getClusterState();
}
@@ -90,30 +82,6 @@ class SimpleClusterAbstractionsImpl {
}
}
- private static Set<String> filterNonDataNodes(SolrCloudManager solrCloudManager) {
- Set<String> liveNodes = solrCloudManager.getClusterStateProvider().getLiveNodes();
- AtomicReference<Set<String>> liveNodesCopy = new AtomicReference<>(liveNodes);
- try {
- solrCloudManager.getDistribStateManager().forEachChild(ZkStateReader.NODE_ROLES, (name, data) -> {
- if(data != null && data.getData() != null && data.getData().length >0) {
- @SuppressWarnings("unchecked")
- Map<String,Object> map = (Map<String, Object>) Utils.fromJSON(data.getData());
- if(Boolean.FALSE.equals(map.get("hasData"))) {
- if(liveNodesCopy.get() == liveNodes) {
- //the map provided should not be modified. So we make a copy
- liveNodesCopy.set(new HashSet<>(liveNodes));
- }
- liveNodesCopy.get().remove(name);
- }
- }
- });
-
- } catch (Exception e) {
- throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unable to communicate with Zookeeper");
- }
- return liveNodesCopy.get();
- }
-
static class NodeImpl implements Node {
public final String nodeName;
diff --git a/solr/core/src/java/org/apache/solr/core/CoreContainer.java b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
index f2c2b78..f85f3d7 100644
--- a/solr/core/src/java/org/apache/solr/core/CoreContainer.java
+++ b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
@@ -246,7 +246,7 @@ public class CoreContainer {
private final ObjectCache objectCache = new ObjectCache();
- public final NodeRole nodeRole = new NodeRole(System.getProperty("node.roles"));
+ public final NodeRole nodeRole = new NodeRole(System.getProperty(NodeRole.NODE_ROLE));
private final ClusterSingletons clusterSingletons = new ClusterSingletons(
() -> getZkController() != null &&
diff --git a/solr/core/src/java/org/apache/solr/core/NodeRole.java b/solr/core/src/java/org/apache/solr/core/NodeRole.java
index 7feb742..f0dd794 100644
--- a/solr/core/src/java/org/apache/solr/core/NodeRole.java
+++ b/solr/core/src/java/org/apache/solr/core/NodeRole.java
@@ -31,10 +31,7 @@ public class NodeRole implements MapWriter {
return;
}
Set<String> roles = new HashSet<>(StrUtils.split(role, ','));
- if (roles.isEmpty()) {
- hasData = true;
- return;
- }
+ if (roles.isEmpty()) return;
for (String s : roles) {
if (Type.data.name().equals(s)) {
hasData = true;
@@ -42,6 +39,7 @@ public class NodeRole implements MapWriter {
}
if (Type.overseer.name().equals(s)) {
this.role = Type.overseer;
+ hasData = false;
} else {
throw new RuntimeException("Unknown type");
}
@@ -59,16 +57,13 @@ public class NodeRole implements MapWriter {
@Override
public void writeMap(EntryWriter ew) throws IOException {
- ew.put("hasData", hasData);
+ ew.put(HAS_DATA, hasData);
ew.put("role", role.name());
}
public enum Type {
data, overseer;
}
-
-
-
- public static final String NO_REPLICAS = "no-replicas";
-
+ public static final String HAS_DATA = "hasData";
+ public static final String NODE_ROLE = "solr.node.role";
}
diff --git a/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java b/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
index 6f6773d..74b221f 100644
--- a/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
+++ b/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
@@ -75,6 +75,7 @@ public class ClusterAPI {
@EndPoint(method = GET,
path = "/cluster/node-roles",
permission = COLL_READ_PERM)
+ @SuppressWarnings("unchecked")
public void roles(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
Map <String, Map<String,Object>> result = new LinkedHashMap<>();
collectionsHandler.getCoreContainer().getZkController().getSolrCloudManager().getDistribStateManager().
@@ -89,7 +90,7 @@ public class ClusterAPI {
@EndPoint(method = GET,
path = "/cluster/node-roles/{role}",
permission = COLL_READ_PERM)
- public void rolesOfNode(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
+ public void nodesWithRole(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
String role = req.getPathTemplateValues().get("role");
List<String> result = new ArrayList<>();
diff --git a/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java b/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java
new file mode 100644
index 0000000..70ab96b
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java
@@ -0,0 +1,97 @@
+package org.apache.solr.cloud;
+
+import java.lang.invoke.MethodHandles;
+import java.util.List;
+import org.apache.solr.client.solrj.embedded.JettySolrRunner;
+import org.apache.solr.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.client.solrj.request.V2Request;
+import org.apache.solr.client.solrj.response.V2Response;
+import org.apache.solr.core.NodeRole;
+import org.junit.After;
+import org.junit.Before;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class NodeRolesTest extends SolrCloudTestCase {
+ private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ @Before
+ public void setupCluster() throws Exception {
+ configureCluster(1)
+ .addConfig("conf", configset("cloud-minimal"))
+ .configure();
+ }
+
+ @After
+ public void tearDownCluster() throws Exception {
+ shutdownCluster();
+ }
+
+ @SuppressWarnings("unchecked")
+ public void testRoleIntegration() throws Exception {
+
+ JettySolrRunner j0 = cluster.getJettySolrRunner(0);
+ JettySolrRunner j1 = null, j2 = null;
+ System.setProperty(NodeRole.NODE_ROLE, "overseer");
+ try {
+ j1 = cluster.startJettySolrRunner();
+ } finally {
+ System.clearProperty(NodeRole.NODE_ROLE);
+ }
+
+ V2Response rsp = new V2Request
+ .Builder("/cluster/node-roles")
+ .GET()
+ .build()
+ .process(cluster.getSolrClient());
+ assertEquals(Boolean.FALSE, rsp._get(List.of("node-roles", j1.getNodeName(), NodeRole.HAS_DATA), null));
+ assertEquals("overseer", rsp._get(List.of("node-roles", j1.getNodeName(), "role"), null));
+
+ OverseerRolesTest.waitForNewOverseer(20, j1.getNodeName(), false);
+ //start another node that is overseer but has data
+ System.setProperty(NodeRole.NODE_ROLE, "overseer,data");
+ try {
+ j2 = cluster.startJettySolrRunner();
+ } finally {
+ System.clearProperty(NodeRole.NODE_ROLE);
+ }
+ rsp = new V2Request
+ .Builder("/cluster/node-roles")
+ .GET()
+ .build()
+ .process(cluster.getSolrClient());
+
+ assertEquals(Boolean.TRUE, rsp._get(List.of("node-roles", j2.getNodeName(), NodeRole.HAS_DATA), null));
+ assertEquals("overseer", rsp._get(List.of("node-roles", j2.getNodeName(), "role"), null));
+
+ rsp = new V2Request
+ .Builder("/cluster/node-roles/overseer")
+ .GET()
+ .build()
+ .process(cluster.getSolrClient());
+ List<String> overseerNodes = (List<String>) rsp._get("nodes", null);
+ assertTrue(overseerNodes.contains(j1.getNodeName()));
+ assertTrue(overseerNodes.contains(j2.getNodeName()));
+
+
+ String COLLECTION_NAME = "TEST_ROLES";
+ CollectionAdminRequest
+ .createCollection(COLLECTION_NAME, "conf", 3, 1)
+ .process(cluster.getSolrClient());
+ cluster.waitForActiveCollection(COLLECTION_NAME, 3, 3);
+
+ String overseer = j1.getNodeName();
+ cluster.getSolrClient().getClusterStateProvider().getCollection(COLLECTION_NAME)
+ .forEachReplica((s, replica) -> assertNotEquals(replica.node, overseer));
+
+ //now shutdown the overseer
+ j1.stop();
+ rsp = new V2Request
+ .Builder("/cluster/node-roles")
+ .GET()
+ .build()
+ .process(cluster.getSolrClient());
+ assertNull(rsp._get(List.of("node-roles", overseer), null));
+ }
+
+}
diff --git a/solr/core/src/test/org/apache/solr/cloud/OverseerRolesTest.java b/solr/core/src/test/org/apache/solr/cloud/OverseerRolesTest.java
index c437a30..508e74e 100644
--- a/solr/core/src/test/org/apache/solr/cloud/OverseerRolesTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/OverseerRolesTest.java
@@ -54,7 +54,7 @@ public class OverseerRolesTest extends SolrCloudTestCase {
shutdownCluster();
}
- private void waitForNewOverseer(int seconds, Predicate<String> state, boolean failOnIntermediateTransition) throws Exception {
+ private static void waitForNewOverseer(int seconds, Predicate<String> state, boolean failOnIntermediateTransition) throws Exception {
TimeOut timeout = new TimeOut(seconds, TimeUnit.SECONDS, TimeSource.NANO_TIME);
String current = null;
while (timeout.hasTimedOut() == false) {
@@ -72,7 +72,7 @@ public class OverseerRolesTest extends SolrCloudTestCase {
fail("Timed out waiting for overseer state change. The current overseer is: "+current);
}
- private void waitForNewOverseer(int seconds, String expected, boolean failOnIntermediateTransition) throws Exception {
+ public static void waitForNewOverseer(int seconds, String expected, boolean failOnIntermediateTransition) throws Exception {
log.info("Expecting node: {}", expected);
waitForNewOverseer(seconds, s -> Objects.equals(s, expected), failOnIntermediateTransition);
}
[solr] 04/28: suppress warnings
Posted by is...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
ishan pushed a commit to branch jira/SOLR15694
in repository https://gitbox.apache.org/repos/asf/solr.git
commit 907d1f1f3395b55bd69e4beb6b4372e7c2807a96
Author: Noble Paul <no...@gmail.com>
AuthorDate: Wed Nov 10 17:08:13 2021 +1100
suppress warnings
---
.../solr/cluster/placement/impl/SimpleClusterAbstractionsImpl.java | 1 +
1 file changed, 1 insertion(+)
diff --git a/solr/core/src/java/org/apache/solr/cluster/placement/impl/SimpleClusterAbstractionsImpl.java b/solr/core/src/java/org/apache/solr/cluster/placement/impl/SimpleClusterAbstractionsImpl.java
index 1d5bec6..d7453d6 100644
--- a/solr/core/src/java/org/apache/solr/cluster/placement/impl/SimpleClusterAbstractionsImpl.java
+++ b/solr/core/src/java/org/apache/solr/cluster/placement/impl/SimpleClusterAbstractionsImpl.java
@@ -61,6 +61,7 @@ class SimpleClusterAbstractionsImpl {
private final Set<Node> liveNodes;
private final ClusterState clusterState;
+ @SuppressWarnings("unchecked")
ClusterImpl(SolrCloudManager solrCloudManager) throws IOException {
Set<String> liveNodes = solrCloudManager.getClusterStateProvider().getLiveNodes();
try {
[solr] 28/28: cleanup and more test assertions
Posted by is...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
ishan pushed a commit to branch jira/SOLR15694
in repository https://gitbox.apache.org/repos/asf/solr.git
commit 60c41705e38fb74000708e730799dff5c5b6b51b
Author: Noble Paul <no...@gmail.com>
AuthorDate: Tue Dec 28 13:41:22 2021 +1100
cleanup and more test assertions
---
.../src/java/org/apache/solr/core/NodeRoles.java | 19 +++---
.../test/org/apache/solr/cloud/NodeRolesTest.java | 67 +++++++++++++---------
.../org/apache/solr/cloud/OverseerRolesTest.java | 2 +-
solr/solr-ref-guide/src/node-roles.adoc | 2 -
.../apache/solr/common/cloud/ZkStateReader.java | 2 +-
5 files changed, 49 insertions(+), 43 deletions(-)
diff --git a/solr/core/src/java/org/apache/solr/core/NodeRoles.java b/solr/core/src/java/org/apache/solr/core/NodeRoles.java
index 317f771..2eb2b62 100644
--- a/solr/core/src/java/org/apache/solr/core/NodeRoles.java
+++ b/solr/core/src/java/org/apache/solr/core/NodeRoles.java
@@ -30,12 +30,9 @@ public class NodeRoles {
public static final String NODE_ROLES_PROP = "solr.node.roles";
- /*public static final String ON = "on";
- public static final String OFF = "off";
- public static final String ALLOWED = "allowed";
- public static final String DISALLOWED = "disallowed";
- public static final String PREFERRED = "preferred";*/
-
+ /**
+ * Roles to be assumed on nodes that don't have roles specified for them at startup
+ */
public static final String DEFAULT_ROLES_STRING = "data:on,overseer:allowed";
// Map of roles to mode that are applicable for this node.
@@ -60,7 +57,7 @@ public class NodeRoles {
}
for(Role r: Role.values()) {
if (!roles.containsKey(r)) {
- roles.put(r, r.defaultMode());
+ roles.put(r, r.modeWhenRoleIsAbsent());
}
}
nodeRoles = Collections.unmodifiableMap(roles);
@@ -82,11 +79,11 @@ public class NodeRoles {
public enum Mode {
ON, OFF, ALLOWED, PREFERRED, DISALLOWED;
- @Override
/**
* Need this lowercasing so that the ZK references use the lowercase form, which is
* also the form documented in user facing documentation.
*/
+ @Override
public String toString() {
return name().toLowerCase(Locale.ROOT);
}
@@ -99,7 +96,7 @@ public class NodeRoles {
return Set.of(Mode.ON, Mode.OFF);
}
@Override
- public Mode defaultMode() {
+ public Mode modeWhenRoleIsAbsent() {
return Mode.OFF;
}
},
@@ -109,7 +106,7 @@ public class NodeRoles {
return Set.of(Mode.ALLOWED, Mode.PREFERRED, Mode.DISALLOWED);
}
@Override
- public Mode defaultMode() {
+ public Mode modeWhenRoleIsAbsent() {
return Mode.DISALLOWED;
}
};
@@ -132,7 +129,7 @@ public class NodeRoles {
/**
* Default mode for a role in nodes where this role is not specified.
*/
- public abstract Mode defaultMode();
+ public abstract Mode modeWhenRoleIsAbsent();
@Override
public String toString() {
diff --git a/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java b/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java
index 0fc0e95..fb05e34 100644
--- a/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java
@@ -21,7 +21,6 @@ import java.lang.invoke.MethodHandles;
import java.util.Collection;
import java.util.Collections;
-import java.util.List;
import java.util.Map;
import org.apache.solr.client.solrj.embedded.JettySolrRunner;
@@ -49,37 +48,25 @@ public class NodeRolesTest extends SolrCloudTestCase {
shutdownCluster();
}
- @SuppressWarnings("unchecked")
+ @SuppressWarnings("rawtypes")
public void testRoleIntegration() throws Exception {
JettySolrRunner j0 = cluster.getJettySolrRunner(0);
- JettySolrRunner j1, j2;
- V2Response rsp = new V2Request.Builder("/cluster/node-roles/supported").GET().build().process(cluster.getSolrClient());
- Map<String, Object> l = (Map<String, Object>) rsp._get("supported-roles", Collections.emptyMap());
- assertTrue(l.containsKey("data"));
- assertTrue(l.containsKey("overseer"));
+ testSupportedRolesAPI();
- j1 = startNodeWithRoles("overseer:preferred,data:off");
+ // Start a dedicated overseer node
+ JettySolrRunner j1 = startNodeWithRoles("overseer:preferred,data:off");
+ validateNodeRoles(j1.getNodeName(), "node-roles/overseer/preferred", j1.getNodeName(), "node-roles/data/off");
- rsp = new V2Request.Builder("/cluster/node-roles").GET().build().process(cluster.getSolrClient());
- assertEquals(j1.getNodeName(), rsp._getStr("node-roles/overseer/preferred[0]", null));
- assertEquals(j1.getNodeName(), rsp._getStr("node-roles/data/off[0]", null));
- OverseerRolesTest.waitForNewOverseer(20, j1.getNodeName(), false);
+ V2Response rsp;
+ OverseerRolesTest.waitForNewOverseer(20, j1.getNodeName(), true);
- // start another node that is overseer but has data
- j2 = startNodeWithRoles("overseer:preferred,data:on");
- rsp = new V2Request.Builder("/cluster/node-roles").GET().build().process(cluster.getSolrClient());
+ // Start another node that is allowed or preferred overseer but has data
+ String overseerModeOnDataNode = random().nextBoolean() ? "preferred" : "allowed";
+ JettySolrRunner j2 = startNodeWithRoles("overseer:" + overseerModeOnDataNode + ",data:on");
+ validateNodeRoles(j2.getNodeName(), "node-roles/overseer/" + overseerModeOnDataNode, j2.getNodeName(), "node-roles/data/on");
- assertTrue( ((Collection)rsp._get("node-roles/overseer/preferred", Collections.emptyList())).contains(j2.getNodeName()));
- assertTrue( ((Collection)rsp._get("node-roles/data/on", Collections.emptyList())).contains(j2.getNodeName()));
-
- rsp = new V2Request.Builder("/cluster/node-roles/role/overseer").GET().build().process(cluster.getSolrClient());
-
- assertTrue( ((Collection)rsp._get("node-roles/overseer/preferred", Collections.emptyList())).contains(j2.getNodeName()));
- assertTrue( ((Collection)rsp._get("node-roles/overseer/preferred", Collections.emptyList())).contains(j1.getNodeName()));
-
- rsp = new V2Request.Builder("/cluster/node-roles/role/overseer/preferred").GET().build().process(cluster.getSolrClient());
- assertTrue( ((Collection)rsp._get("node-roles/overseer/preferred", Collections.emptyList())).contains(j2.getNodeName()));
- assertTrue( ((Collection)rsp._get("node-roles/overseer/preferred", Collections.emptyList())).contains(j1.getNodeName()));
+ // validate the preferred overseers
+ validateNodeRoles(j2.getNodeName(), "node-roles/overseer/" + overseerModeOnDataNode, j1.getNodeName(), "node-roles/overseer/preferred");
String COLLECTION_NAME = "TEST_ROLES";
CollectionAdminRequest
@@ -94,8 +81,32 @@ public class NodeRolesTest extends SolrCloudTestCase {
// Shutdown the dedicated overseer, make sure that node disappears from the roles output
j1.stop();
- rsp = new V2Request.Builder("/cluster/node-roles/role/overseer/preferred").GET().build().process(cluster.getSolrClient());
- assertFalse (((Collection) rsp._get("node-roles/overseer/preferred" , null)). contains(j1.getNodeName()));
+
+ // Wait and make sure that another node picks up overseer responsibilities
+ OverseerRolesTest.waitForNewOverseer(20, it -> !dedicatedOverseer.equals(it), false);
+
+ // Make sure the stopped node no longer has the role assigned
+ rsp = new V2Request.Builder("/cluster/node-roles/role/overseer/" + overseerModeOnDataNode).GET().build().process(cluster.getSolrClient());
+ assertFalse(((Collection) rsp._get("node-roles/overseer/" + overseerModeOnDataNode, null)).contains(j1.getNodeName()));
+ }
+
+ @SuppressWarnings("rawtypes")
+ private void validateNodeRoles(String... nodenamePaths) throws org.apache.solr.client.solrj.SolrServerException, java.io.IOException {
+ V2Response rsp = new V2Request.Builder("/cluster/node-roles").GET().build().process(cluster.getSolrClient());
+ for (int i = 0; i < nodenamePaths.length; i += 2) {
+ String nodename = nodenamePaths[i];
+ String path = nodenamePaths[i + 1];
+ assertTrue("Didn't find " + nodename + " at " + path + ". Full response: " + rsp.jsonStr(),
+ ((Collection) rsp._get(path, Collections.emptyList())).contains(nodename));
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private void testSupportedRolesAPI() throws Exception {
+ V2Response rsp = new V2Request.Builder("/cluster/node-roles/supported").GET().build().process(cluster.getSolrClient());
+ Map<String, Object> l = (Map<String, Object>) rsp._get("supported-roles", Collections.emptyMap());
+ assertTrue(l.containsKey("data"));
+ assertTrue(l.containsKey("overseer"));
}
private JettySolrRunner startNodeWithRoles(String roles) throws Exception {
diff --git a/solr/core/src/test/org/apache/solr/cloud/OverseerRolesTest.java b/solr/core/src/test/org/apache/solr/cloud/OverseerRolesTest.java
index 508e74e..6e59f09 100644
--- a/solr/core/src/test/org/apache/solr/cloud/OverseerRolesTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/OverseerRolesTest.java
@@ -54,7 +54,7 @@ public class OverseerRolesTest extends SolrCloudTestCase {
shutdownCluster();
}
- private static void waitForNewOverseer(int seconds, Predicate<String> state, boolean failOnIntermediateTransition) throws Exception {
+ public static void waitForNewOverseer(int seconds, Predicate<String> state, boolean failOnIntermediateTransition) throws Exception {
TimeOut timeout = new TimeOut(seconds, TimeUnit.SECONDS, TimeSource.NANO_TIME);
String current = null;
while (timeout.hasTimedOut() == false) {
diff --git a/solr/solr-ref-guide/src/node-roles.adoc b/solr/solr-ref-guide/src/node-roles.adoc
index 455de14..aa36fe6e3 100644
--- a/solr/solr-ref-guide/src/node-roles.adoc
+++ b/solr/solr-ref-guide/src/node-roles.adoc
@@ -93,12 +93,10 @@ curl http://localhost:8983/api/cluster/node-roles/supported
{
"supported-roles":{
"data":{
- "defaultIfAbsent":"off",
"modes":["off",
"on"]
},
"overseer":{
- "defaultIfAbsent":"disallowed",
"modes":["disallowed",
"allowed",
"preferred"]
diff --git a/solr/solrj/src/java/org/apache/solr/common/cloud/ZkStateReader.java b/solr/solrj/src/java/org/apache/solr/common/cloud/ZkStateReader.java
index 8814cc9..d35bd8f 100644
--- a/solr/solrj/src/java/org/apache/solr/common/cloud/ZkStateReader.java
+++ b/solr/solrj/src/java/org/apache/solr/common/cloud/ZkStateReader.java
@@ -108,7 +108,7 @@ public class ZkStateReader implements SolrCloseable {
* The following, node_roles and roles.json are for assigning roles to
* nodes. The node_roles is the preferred way (using -Dsolr.node.roles param),
* and roles.json is used by legacy ADDROLE API command.
- * TODO: Deprecate & remove support for roles.json in an upcoming release.
+ * TODO: Deprecate and remove support for roles.json in an upcoming release.
*/
public static final String NODE_ROLES = "/node_roles";
public static final String ROLES = "/roles.json";
[solr] 21/28: Adding Roles API to ref guide
Posted by is...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
ishan pushed a commit to branch jira/SOLR15694
in repository https://gitbox.apache.org/repos/asf/solr.git
commit 6ffba2ac5f30e428e3ed76f4de28bd320f6609d4
Author: Ishan Chattopadhyaya <is...@apache.org>
AuthorDate: Sat Dec 18 15:12:13 2021 +0530
Adding Roles API to ref guide
---
.../java/org/apache/solr/handler/ClusterAPI.java | 42 ++++++++--
.../test/org/apache/solr/cloud/NodeRolesTest.java | 18 ++--
solr/solr-ref-guide/src/node-roles.adoc | 97 +++++++++++++++++++++-
3 files changed, 140 insertions(+), 17 deletions(-)
diff --git a/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java b/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
index 9f93c39..39f4750 100644
--- a/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
+++ b/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
@@ -103,10 +103,8 @@ public class ClusterAPI {
}
for (String child : children) {
Object c = readRecursive(path + "/" + child, zk, depth - 1);
-
result.put(child, c);
}
-
} catch (Exception e) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e);
}
@@ -115,7 +113,6 @@ public class ClusterAPI {
} else {
return result;
}
-
}
@EndPoint(method = GET,
@@ -123,16 +120,45 @@ public class ClusterAPI {
permission = COLL_READ_PERM)
public void nodesWithRole(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
String role = req.getPathTemplateValues().get("role");
- rsp.add(role,
+ rsp.add("node-roles", Map.of(role,
readRecursive(ZkStateReader.NODE_ROLES + "/"+ role,
- collectionsHandler.getCoreContainer().getZkController().getSolrCloudManager().getDistribStateManager(), 2));
+ collectionsHandler.getCoreContainer().getZkController().getSolrCloudManager().getDistribStateManager(), 2)));
+ }
+
+ @EndPoint(method = GET,
+ path = "/cluster/node-roles/nodes/{node}",
+ permission = COLL_READ_PERM)
+ @SuppressWarnings("unchecked")
+ public void rolesForNode(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
+ String node = req.getPathTemplateValues().get("node");
+ Map<String, String> ret = new HashMap<String, String>();
+ Map<String, Map<String, Set<String>>> roles = (Map<String, Map<String, Set<String>>>) readRecursive(ZkStateReader.NODE_ROLES,
+ collectionsHandler.getCoreContainer().getZkController().getSolrCloudManager().getDistribStateManager(), 3);
+ for (String role: roles.keySet()) {
+ for (String mode : roles.get(role).keySet()) {
+ if (roles.get(role).get(mode) instanceof List && ((List) roles.get(role).get(mode)).size() == 0) {
+ continue;
+ }
+ Set<String> nodes = roles.get(role).get(mode);
+ if (nodes.contains(node)) ret.put(role, mode);
+ }
+ }
+ for (String role: ret.keySet()) {
+ rsp.add(role, ret.get(role));
+ }
}
+
@EndPoint(method = GET,
path = "/cluster/node-roles/supported",
permission = COLL_READ_PERM)
public void supportedRoles(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
- rsp.add("supported-roles",
- collectionsHandler.getCoreContainer().getZkController().getSolrCloudManager().getDistribStateManager().listData(ZkStateReader.NODE_ROLES));
+ //rsp.add("supported-roles",
+ // collectionsHandler.getCoreContainer().getZkController().getSolrCloudManager().getDistribStateManager().listData(ZkStateReader.NODE_ROLES));
+ Map<String, Object> roleModesSupportedMap = new HashMap<>();
+ for (NodeRoles.Role role: NodeRoles.Role.values()) {
+ roleModesSupportedMap.put(role.toString(), Map.of("modes", role.supportedModes(), "defaultIfAbsent", role.defaultIfAbsent()));
+ }
+ rsp.add("supported-roles", roleModesSupportedMap);
}
@EndPoint(method = GET,
@@ -143,7 +169,7 @@ public class ClusterAPI {
String roleVal = req.getPathTemplateValues().get("role-val");
List<String> nodes = collectionsHandler.getCoreContainer().getZkController().getSolrCloudManager()
.getDistribStateManager().listData(ZkStateReader.NODE_ROLES + "/"+ role+"/"+roleVal);
- rsp.add( role, Collections.singletonMap(roleVal, nodes));
+ rsp.add( "node-roles", Map.of(role, Collections.singletonMap(roleVal, nodes)));
}
public static List<String> getNodesByRole(NodeRoles.Role role, String val, DistribStateManager zk)
diff --git a/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java b/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java
index cda83ad..1970f6c 100644
--- a/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java
@@ -22,6 +22,8 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
+
import org.apache.solr.client.solrj.embedded.JettySolrRunner;
import org.apache.solr.client.solrj.request.CollectionAdminRequest;
import org.apache.solr.client.solrj.request.V2Request;
@@ -52,9 +54,9 @@ public class NodeRolesTest extends SolrCloudTestCase {
JettySolrRunner j0 = cluster.getJettySolrRunner(0);
JettySolrRunner j1 = null, j2 = null;
V2Response rsp = new V2Request.Builder("/cluster/node-roles/supported").GET().build().process(cluster.getSolrClient());
- List<String> l = (List<String>) rsp._get("supported-roles", Collections.emptyList());
- assertTrue(l.contains("data"));
- assertTrue(l.contains("overseer"));
+ Map<String, Object> l = (Map<String, Object>) rsp._get("supported-roles", Collections.emptyMap());
+ assertTrue(l.containsKey("data"));
+ assertTrue(l.containsKey("overseer"));
j1 = startNodeWithRoles("overseer:preferred,data:off");
@@ -72,12 +74,12 @@ public class NodeRolesTest extends SolrCloudTestCase {
rsp = new V2Request.Builder("/cluster/node-roles/overseer").GET().build().process(cluster.getSolrClient());
- assertTrue( ((Collection)rsp._get("overseer/preferred", Collections.emptyList())).contains(j2.getNodeName()));
- assertTrue( ((Collection)rsp._get("overseer/preferred", Collections.emptyList())).contains(j1.getNodeName()));
+ assertTrue( ((Collection)rsp._get("node-roles/overseer/preferred", Collections.emptyList())).contains(j2.getNodeName()));
+ assertTrue( ((Collection)rsp._get("node-roles/overseer/preferred", Collections.emptyList())).contains(j1.getNodeName()));
rsp = new V2Request.Builder("/cluster/node-roles/overseer/preferred").GET().build().process(cluster.getSolrClient());
- assertTrue( ((Collection)rsp._get("overseer/preferred", Collections.emptyList())).contains(j2.getNodeName()));
- assertTrue( ((Collection)rsp._get("overseer/preferred", Collections.emptyList())).contains(j1.getNodeName()));
+ assertTrue( ((Collection)rsp._get("node-roles/overseer/preferred", Collections.emptyList())).contains(j2.getNodeName()));
+ assertTrue( ((Collection)rsp._get("node-roles/overseer/preferred", Collections.emptyList())).contains(j1.getNodeName()));
String COLLECTION_NAME = "TEST_ROLES";
CollectionAdminRequest
@@ -93,7 +95,7 @@ public class NodeRolesTest extends SolrCloudTestCase {
// Shutdown the dedicated overseer, make sure that node disappears from the roles output
j1.stop();
rsp = new V2Request.Builder("/cluster/node-roles/overseer/preferred").GET().build().process(cluster.getSolrClient());
- assertFalse (((Collection) rsp._get("overseer/preferred" , null)). contains(j1.getNodeName()));
+ assertFalse (((Collection) rsp._get("node-roles/overseer/preferred" , null)). contains(j1.getNodeName()));
}
private JettySolrRunner startNodeWithRoles(String roles) throws Exception {
diff --git a/solr/solr-ref-guide/src/node-roles.adoc b/solr/solr-ref-guide/src/node-roles.adoc
index f3e5969..ed2c07a 100644
--- a/solr/solr-ref-guide/src/node-roles.adoc
+++ b/solr/solr-ref-guide/src/node-roles.adoc
@@ -62,4 +62,99 @@ A common situation arises when the nodes in a cluster is under heavy querying or
* Most nodes, say 10 of them, in the cluster start with `-Dsolr.node.roles=data:on,overseer:allowed` (or with no parameter, since the default value for `solr.node.roles` is the same).
* One or more nodes can start with `-Dsolr.node.roles=overseer:preferred` (or `-Dsolr.node.roles=overseer:preferred,data:off`)
-In this arrangement, such dedicated nodes can have lesser resources than other data nodes (since these are stateless nodes) and yet the cluster will behave optimally. In case the dedicated overseer nodes go down for some reason, the overseer leader will be elected from one of the data nodes (since they have overseer in "allowed" mode), and once one of the dedicated overseer nodes are back up, it will assume the overseer leadership again.
\ No newline at end of file
+In this arrangement, such dedicated nodes can have lesser resources than other data nodes (since these are stateless nodes) and yet the cluster will behave optimally. In case the dedicated overseer nodes go down for some reason, the overseer leader will be elected from one of the data nodes (since they have overseer in "allowed" mode), and once one of the dedicated overseer nodes are back up, it will assume the overseer leadership again.
+
+== Roles API
+
+=== GET /api/cluster/node-roles/supported
+
+*Input*
+[source,text]
+----
+curl http://localhost:8983/api/cluster/node-roles/supported
+----
+
+*Output*
+[source,text]
+----
+{
+ "supported-roles":{
+ "data":{
+ "defaultIfAbsent":"off",
+ "modes":["off",
+ "on"]
+ },
+ "overseer":{
+ "defaultIfAbsent":"disallowed",
+ "modes":["disallowed",
+ "allowed",
+ "preferred"]
+ }
+ }
+}
+----
+
+=== GET /api/cluster/node-roles
+
+*Input*
+[source,text]
+----
+curl http://localhost:8983/api/cluster/node-roles
+----
+
+*Output*
+[source,text]
+----
+{
+ "node-roles":{
+ "data":{
+ "off":["solr2:8983_solr"],
+ "on":["solr1:8983_solr"]
+ },
+ "overseer":{
+ "allowed":["solr1:8983_solr"],
+ "disallowed":[],
+ "preferred":["solr2:8983_solr"]
+ }
+ }
+}
+----
+
+=== GET /api/cluster/node-roles/{role}
+
+*Input*
+[source,text]
+----
+http://localhost:8983/api/node-roles/data
+----
+
+*Output*
+[source,text]
+----
+{
+ "node-roles":{
+ "data":{
+ "off":["solr2:8983_solr"],
+ "on":["solr1:8983_solr"]
+ }
+ }
+}
+----
+
+
+=== GET /api/cluster/node-roles/nodes/{node}
+
+*Input*
+[source,text]
+----
+curl http://localhost:8983/api/cluster/node-roles/nodes/solr1:8983_solr
+----
+
+*Output*
+[source,text]
+----
+{
+ "data":"on",
+ "overseer":"allowed"
+}
+----
[solr] 25/28: rearranged API paths
Posted by is...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
ishan pushed a commit to branch jira/SOLR15694
in repository https://gitbox.apache.org/repos/asf/solr.git
commit 483f9849006b995d36dfaaf6099e8756f4b9e5b9
Author: Noble Paul <no...@gmail.com>
AuthorDate: Sun Dec 19 15:24:39 2021 +1100
rearranged API paths
---
.../java/org/apache/solr/handler/ClusterAPI.java | 10 ++++----
.../test/org/apache/solr/cloud/NodeRolesTest.java | 6 ++---
solr/solr-ref-guide/src/node-roles.adoc | 27 ++++++++++++++++++----
3 files changed, 30 insertions(+), 13 deletions(-)
diff --git a/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java b/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
index 39f4750..6a0148e 100644
--- a/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
+++ b/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
@@ -116,7 +116,7 @@ public class ClusterAPI {
}
@EndPoint(method = GET,
- path = "/cluster/node-roles/{role}",
+ path = "/cluster/node-roles/role/{role}",
permission = COLL_READ_PERM)
public void nodesWithRole(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
String role = req.getPathTemplateValues().get("role");
@@ -126,7 +126,7 @@ public class ClusterAPI {
}
@EndPoint(method = GET,
- path = "/cluster/node-roles/nodes/{node}",
+ path = "/cluster/node-roles/node/{node}",
permission = COLL_READ_PERM)
@SuppressWarnings("unchecked")
public void rolesForNode(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
@@ -152,9 +152,7 @@ public class ClusterAPI {
path = "/cluster/node-roles/supported",
permission = COLL_READ_PERM)
public void supportedRoles(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
- //rsp.add("supported-roles",
- // collectionsHandler.getCoreContainer().getZkController().getSolrCloudManager().getDistribStateManager().listData(ZkStateReader.NODE_ROLES));
- Map<String, Object> roleModesSupportedMap = new HashMap<>();
+ Map<String, Object> roleModesSupportedMap = new HashMap<>();
for (NodeRoles.Role role: NodeRoles.Role.values()) {
roleModesSupportedMap.put(role.toString(), Map.of("modes", role.supportedModes(), "defaultIfAbsent", role.defaultIfAbsent()));
}
@@ -162,7 +160,7 @@ public class ClusterAPI {
}
@EndPoint(method = GET,
- path = "/cluster/node-roles/{role}/{role-val}",
+ path = "/cluster/node-roles/role/{role}/{role-val}",
permission = COLL_READ_PERM)
public void nodesWithRoleVal(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
String role = req.getPathTemplateValues().get("role");
diff --git a/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java b/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java
index 1970f6c..63f7d86 100644
--- a/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java
@@ -72,12 +72,12 @@ public class NodeRolesTest extends SolrCloudTestCase {
assertTrue( ((Collection)rsp._get("node-roles/overseer/preferred", Collections.emptyList())).contains(j2.getNodeName()));
assertTrue( ((Collection)rsp._get("node-roles/data/on", Collections.emptyList())).contains(j2.getNodeName()));
- rsp = new V2Request.Builder("/cluster/node-roles/overseer").GET().build().process(cluster.getSolrClient());
+ rsp = new V2Request.Builder("/cluster/node-roles/role/overseer").GET().build().process(cluster.getSolrClient());
assertTrue( ((Collection)rsp._get("node-roles/overseer/preferred", Collections.emptyList())).contains(j2.getNodeName()));
assertTrue( ((Collection)rsp._get("node-roles/overseer/preferred", Collections.emptyList())).contains(j1.getNodeName()));
- rsp = new V2Request.Builder("/cluster/node-roles/overseer/preferred").GET().build().process(cluster.getSolrClient());
+ rsp = new V2Request.Builder("/cluster/node-roles/role/overseer/preferred").GET().build().process(cluster.getSolrClient());
assertTrue( ((Collection)rsp._get("node-roles/overseer/preferred", Collections.emptyList())).contains(j2.getNodeName()));
assertTrue( ((Collection)rsp._get("node-roles/overseer/preferred", Collections.emptyList())).contains(j1.getNodeName()));
@@ -94,7 +94,7 @@ public class NodeRolesTest extends SolrCloudTestCase {
// Shutdown the dedicated overseer, make sure that node disappears from the roles output
j1.stop();
- rsp = new V2Request.Builder("/cluster/node-roles/overseer/preferred").GET().build().process(cluster.getSolrClient());
+ rsp = new V2Request.Builder("/cluster/node-roles/role/overseer/preferred").GET().build().process(cluster.getSolrClient());
assertFalse (((Collection) rsp._get("node-roles/overseer/preferred" , null)). contains(j1.getNodeName()));
}
diff --git a/solr/solr-ref-guide/src/node-roles.adoc b/solr/solr-ref-guide/src/node-roles.adoc
index 3e902fa..e5a70ec 100644
--- a/solr/solr-ref-guide/src/node-roles.adoc
+++ b/solr/solr-ref-guide/src/node-roles.adoc
@@ -135,14 +135,14 @@ curl http://localhost:8983/api/cluster/node-roles
}
----
-=== GET /api/cluster/node-roles/{role}
+=== GET /api/cluster/node-roles/role/{role}
Fetches the current node roles assignment for a specified role.
*Input*
[source,text]
----
-http://localhost:8983/api/node-roles/data
+http://localhost:8983/api/node-roles/role/data
----
*Output*
@@ -158,15 +158,34 @@ http://localhost:8983/api/node-roles/data
}
----
+*Input*
+[source,text]
+----
+http://localhost:8983/api/node-roles/role/data/off
+----
+
+*Output*
+[source,text]
+----
+{
+ "node-roles":{
+ "data":{
+ "off":["solr2:8983_solr"]
+ }
+ }
+}
+----
+
+
-=== GET /api/cluster/node-roles/nodes/{node}
+=== GET /api/cluster/node-roles/node/{node}
Fetches the current node roles assignment for the specified node.
*Input*
[source,text]
----
-curl http://localhost:8983/api/cluster/node-roles/nodes/solr1:8983_solr
+curl http://localhost:8983/api/cluster/node-roles/node/solr1:8983_solr
----
*Output*
[solr] 24/28: Fix typo in reg guide
Posted by is...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
ishan pushed a commit to branch jira/SOLR15694
in repository https://gitbox.apache.org/repos/asf/solr.git
commit 7ec11d51084da2330a22cb1858c6b87c372fc64f
Author: Ishan Chattopadhyaya <is...@apache.org>
AuthorDate: Mon Dec 20 16:59:13 2021 +0530
Fix typo in reg guide
---
solr/solr-ref-guide/src/node-roles.adoc | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/solr/solr-ref-guide/src/node-roles.adoc b/solr/solr-ref-guide/src/node-roles.adoc
index 19c5beb..3e902fa 100644
--- a/solr/solr-ref-guide/src/node-roles.adoc
+++ b/solr/solr-ref-guide/src/node-roles.adoc
@@ -45,7 +45,7 @@ Examples: `-Dsolr.node.roles=data:on,overseer:allowed` or `-Dsolr.node.roles=ove
[TIP]
====
-If a node has been started with no `solr.node.roles` parameter, it will be assumed to have the data role turned on and overseer role allowed on it. If you've never needed roles before, you likely won't need to change anything in your startup parameters to accomodate the functionality associated with these roles.
+If a node has been started with no `solr.node.roles` parameter, it will be assumed to have the data role turned on and overseer role allowed on it. If you've never used roles before, you likely won't need to change anything in your startup parameters to accomodate the functionality associated with these roles.
====
.Supported roles
@@ -68,12 +68,12 @@ A node with this role can perform duties of an overseer node (unless mode is `di
== Example usage
-Sometimes, when the nodes in a cluster is under heavy querying or indexing load, the overseer leader node is unable to perform collection management duties efficiently. It might be reasonable to have dedicated nodes to act as the overseer. Such an effect can be achieved as follows:
+Sometimes, when the nodes in a cluster are under heavy querying or indexing load, the overseer leader node might be unable to perform collection management duties efficiently. It might be reasonable to have dedicated nodes to act as the overseer. Such an effect can be achieved as follows:
-* Most nodes, say 10 of them, in the cluster start with `-Dsolr.node.roles=data:on,overseer:allowed` (or with no parameter, since the default value for `solr.node.roles` is the same).
-* One or more nodes can start with `-Dsolr.node.roles=overseer:preferred` (or `-Dsolr.node.roles=overseer:preferred,data:off`)
+* Most nodes (data nodes) in the cluster start with `-Dsolr.node.roles=data:on,overseer:allowed` (or with no parameter, since the default value for `solr.node.roles` is the same).
+* One or more nodes (dedicated overseer nodes) can start with `-Dsolr.node.roles=overseer:preferred` (or `-Dsolr.node.roles=overseer:preferred,data:off`)
-In this arrangement, such dedicated nodes can have lesser resources than other data nodes (since these are stateless nodes) and yet the cluster will behave optimally. In case the dedicated overseer nodes go down for some reason, the overseer leader will be elected from one of the data nodes (since they have overseer in "allowed" mode), and once one of the dedicated overseer nodes are back up, it will assume the overseer leadership again.
+In this arrangement, such dedicated nodes can be provisioned on hardware with lesser resources like CPU, memory or disk space than other data nodes (since these are stateless nodes) and yet the cluster will behave optimally. In case the dedicated overseer nodes go down for some reason, the overseer leader will be elected from one of the data nodes (since they have overseer in "allowed" mode), and once one of the dedicated overseer nodes are back up again, it will be re-elected for the ov [...]
== Roles API
[solr] 18/28: removed unnecessary changes
Posted by is...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
ishan pushed a commit to branch jira/SOLR15694
in repository https://gitbox.apache.org/repos/asf/solr.git
commit 525ff87d6cc39362528d7e4bcc0e0876699777f1
Author: Noble Paul <no...@gmail.com>
AuthorDate: Fri Dec 17 16:37:59 2021 +1100
removed unnecessary changes
---
solr/core/src/java/org/apache/solr/cloud/ZkController.java | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/solr/core/src/java/org/apache/solr/cloud/ZkController.java b/solr/core/src/java/org/apache/solr/cloud/ZkController.java
index 33ea43d..a249e4f 100644
--- a/solr/core/src/java/org/apache/solr/cloud/ZkController.java
+++ b/solr/core/src/java/org/apache/solr/cloud/ZkController.java
@@ -685,9 +685,7 @@ public class ZkController implements Closeable {
} finally {
// just in case the OverseerElectionContext managed to start another Overseer
- if(overseer != null) {
- IOUtils.closeQuietly(overseer);
- }
+ IOUtils.closeQuietly(overseer);
ExecutorUtil.shutdownAndAwaitTermination(customThreadPool);
}
@@ -913,7 +911,7 @@ public class ZkController implements Closeable {
registerLiveNodesListener();
// start the overseer first as following code may need it's processing
- if (!zkRunOnly && cc.nodeRoles.isOverseerAllowed()) {
+ if (!zkRunOnly) {
overseerElector = new LeaderElector(zkClient);
this.overseer = new Overseer((HttpShardHandler) cc.getShardHandlerFactory().getShardHandler(), cc.getUpdateShardHandler(),
CommonParams.CORES_HANDLER_PATH, zkStateReader, this, cloudConfig);
[solr] 05/28: use a separate node
Posted by is...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
ishan pushed a commit to branch jira/SOLR15694
in repository https://gitbox.apache.org/repos/asf/solr.git
commit 16e3ec8cd8e31f5b05fcef0daa3feda4e2893f95
Author: Noble Paul <no...@gmail.com>
AuthorDate: Tue Nov 16 15:58:02 2021 +1100
use a separate node
---
.../java/org/apache/solr/cloud/ZkController.java | 56 ++++++++---------
.../impl/SimpleClusterAbstractionsImpl.java | 59 +++++++++++-------
.../src/java/org/apache/solr/core/NodeRole.java | 70 ++++------------------
.../test/org/apache/solr/core/TestNodeRole.java | 69 ---------------------
.../apache/solr/common/cloud/ZkStateReader.java | 1 +
5 files changed, 76 insertions(+), 179 deletions(-)
diff --git a/solr/core/src/java/org/apache/solr/cloud/ZkController.java b/solr/core/src/java/org/apache/solr/cloud/ZkController.java
index 48c26a4..00274aa 100644
--- a/solr/core/src/java/org/apache/solr/cloud/ZkController.java
+++ b/solr/core/src/java/org/apache/solr/cloud/ZkController.java
@@ -73,7 +73,13 @@ import org.apache.solr.common.util.SolrNamedThreadFactory;
import org.apache.solr.common.util.StrUtils;
import org.apache.solr.common.util.URLUtil;
import org.apache.solr.common.util.Utils;
-import org.apache.solr.core.*;
+import org.apache.solr.core.CloseHook;
+import org.apache.solr.core.CloudConfig;
+import org.apache.solr.core.CoreContainer;
+import org.apache.solr.core.CoreDescriptor;
+import org.apache.solr.core.SolrCore;
+import org.apache.solr.core.SolrCoreInitializationException;
+import org.apache.solr.core.NodeRole;
import org.apache.solr.handler.component.HttpShardHandler;
import org.apache.solr.logging.MDCLoggingContext;
import org.apache.solr.search.SolrIndexSearcher;
@@ -100,7 +106,6 @@ import static org.apache.solr.common.cloud.ZkStateReader.NODE_NAME_PROP;
import static org.apache.solr.common.cloud.ZkStateReader.REJOIN_AT_HEAD_PROP;
import static org.apache.solr.common.cloud.ZkStateReader.SHARD_ID_PROP;
import static org.apache.solr.common.params.CollectionParams.CollectionAction.ADDROLE;
-import static org.apache.solr.common.params.CoreAdminParams.ACTION;
import static org.apache.zookeeper.ZooDefs.Ids.OPEN_ACL_UNSAFE;
/**
@@ -461,7 +466,6 @@ public class ZkController implements Closeable {
});
init();
- persistRoles();
if (distributedClusterStateUpdater.isDistributedStateUpdate()) {
this.overseerJobQueue = null;
@@ -476,28 +480,6 @@ public class ZkController implements Closeable {
assert ObjectReleaseTracker.track(this);
}
- @SuppressWarnings("unchecked")
- private void persistRoles() throws InterruptedException {
- try {
- zkClient.atomicUpdate(ZkStateReader.ROLES,
- bytes -> {
- Map<String, Object> map = bytes == null ? null :
- (Map<String, Object>) Utils.fromJSON(bytes);
- map = cc.nodeRole.modifyRoleData(map,
- getNodeName());
- return map == null ? null : Utils.toJSON(map);
- });
- if (cc.nodeRole.role() == NodeRole.Type.overseer) {
- getOverseerCollectionQueue()
- .offer(Utils.toJSON(Utils.makeMap(ACTION, ADDROLE.name(),
- "role", NodeRole.Type.overseer,
- "node", getNodeName())));
- }
- } catch (KeeperException e) {
- throw new SolrException(ErrorCode.SERVER_ERROR, "can't update roles");
- }
- }
-
/**
* <p>Verifies if /clusterstate.json exists in Zookeepeer, and if it does and is not empty, refuses to start and outputs
* a helpful message regarding collection migration.</p>
@@ -873,6 +855,7 @@ public class ZkController implements Closeable {
throws KeeperException, InterruptedException, IOException {
ZkCmdExecutor cmdExecutor = new ZkCmdExecutor(zkClient.getZkClientTimeout());
cmdExecutor.ensureExists(ZkStateReader.LIVE_NODES_ZKNODE, zkClient);
+ cmdExecutor.ensureExists(ZkStateReader.NODE_ROLES, zkClient);
cmdExecutor.ensureExists(ZkStateReader.COLLECTIONS_ZKNODE, zkClient);
cmdExecutor.ensureExists(ZkStateReader.ALIASES, zkClient);
byte[] emptyJson = "{}".getBytes(StandardCharsets.UTF_8);
@@ -1105,6 +1088,11 @@ public class ZkController implements Closeable {
log.info("Register node as live in ZooKeeper:{}", nodePath);
List<Op> ops = new ArrayList<>(2);
ops.add(Op.create(nodePath, null, zkClient.getZkACLProvider().getACLsToAdd(nodePath), CreateMode.EPHEMERAL));
+ if(cc.nodeRole.role() != NodeRole.Type.data) {
+ //this is a non-data node
+ ops.add(Op.create(ZkStateReader.NODE_ROLES + "/" + nodeName, Utils.toJSON(cc.nodeRole), zkClient.getZkACLProvider().getACLsToAdd(nodePath), CreateMode.EPHEMERAL));
+
+ }
zkClient.multi(ops, true);
}
@@ -2210,6 +2198,10 @@ public class ZkController implements Closeable {
public void checkOverseerDesignate() {
try {
+ if(cc.nodeRole.role() == NodeRole.Type.overseer) {
+ setPreferredOverseer();
+ return;
+ }
byte[] data = zkClient.getData(ZkStateReader.ROLES, null, new Stat(), true);
if (data == null) return;
Map<?,?> roles = (Map<?,?>) Utils.fromJSON(data);
@@ -2217,11 +2209,7 @@ public class ZkController implements Closeable {
List<?> nodeList = (List<?>) roles.get("overseer");
if (nodeList == null) return;
if (nodeList.contains(getNodeName())) {
- ZkNodeProps props = new ZkNodeProps(Overseer.QUEUE_OPERATION, ADDROLE.toString().toLowerCase(Locale.ROOT),
- "node", getNodeName(),
- "role", "overseer");
- log.info("Going to add role {} ", props);
- getOverseerCollectionQueue().offer(Utils.toJSON(props));
+ setPreferredOverseer();
}
} catch (NoNodeException nne) {
return;
@@ -2230,6 +2218,14 @@ public class ZkController implements Closeable {
}
}
+ private void setPreferredOverseer() throws KeeperException, InterruptedException {
+ ZkNodeProps props = new ZkNodeProps(Overseer.QUEUE_OPERATION, ADDROLE.toString().toLowerCase(Locale.ROOT),
+ "node", getNodeName(),
+ "role", "overseer");
+ log.info("Going to add role {} ", props);
+ getOverseerCollectionQueue().offer(Utils.toJSON(props));
+ }
+
public CoreContainer getCoreContainer() {
return cc;
}
diff --git a/solr/core/src/java/org/apache/solr/cluster/placement/impl/SimpleClusterAbstractionsImpl.java b/solr/core/src/java/org/apache/solr/cluster/placement/impl/SimpleClusterAbstractionsImpl.java
index d7453d6..f403aed 100644
--- a/solr/core/src/java/org/apache/solr/cluster/placement/impl/SimpleClusterAbstractionsImpl.java
+++ b/solr/core/src/java/org/apache/solr/cluster/placement/impl/SimpleClusterAbstractionsImpl.java
@@ -33,7 +33,6 @@ import org.apache.solr.common.cloud.ZkStateReader;
import org.apache.solr.common.params.CollectionAdminParams;
import org.apache.solr.common.util.Pair;
import org.apache.solr.common.util.Utils;
-import org.apache.solr.core.NodeRole;
import org.apache.zookeeper.KeeperException;
import javax.annotation.Nonnull;
@@ -61,27 +60,8 @@ class SimpleClusterAbstractionsImpl {
private final Set<Node> liveNodes;
private final ClusterState clusterState;
- @SuppressWarnings("unchecked")
ClusterImpl(SolrCloudManager solrCloudManager) throws IOException {
- Set<String> liveNodes = solrCloudManager.getClusterStateProvider().getLiveNodes();
- try {
- if (solrCloudManager.getDistribStateManager().hasData(ZkStateReader.ROLES)) {
- VersionedData rolesData = solrCloudManager.getDistribStateManager().getData(ZkStateReader.ROLES);
- if (rolesData != null && rolesData.getData().length > 0) {
- Map<String, Object> roles = (Map<String, Object>) Utils.fromJSON(rolesData.getData());
- List<String> noReplicasNodes = (List<String>) roles.get(NodeRole.NO_REPLICAS);
- if (noReplicasNodes != null && !noReplicasNodes.isEmpty()) {
- liveNodes = new HashSet<>(liveNodes);
- liveNodes.removeAll(noReplicasNodes);
- }
- }
- }
-
- } catch (NoSuchElementException nsee) {
- //no problem
- } catch (Exception e) {
- throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unable to communicate with Zookeeper");
- }
+ Set<String> liveNodes = filterNonDataNodes(solrCloudManager);
this.liveNodes = NodeImpl.getNodes(liveNodes);
clusterState = solrCloudManager.getClusterStateProvider().getClusterState();
}
@@ -108,6 +88,43 @@ class SimpleClusterAbstractionsImpl {
}
}
+ private static Set<String> filterNonDataNodes(SolrCloudManager solrCloudManager) {
+ Set<String> liveNodes = solrCloudManager.getClusterStateProvider().getLiveNodes();
+ Set<String> liveNodesCopy = liveNodes;
+ try {
+ List<String> nodesWithRoles = solrCloudManager.getDistribStateManager().listData(ZkStateReader.NODE_ROLES);
+ if(!nodesWithRoles.isEmpty()) {
+ for (String node : nodesWithRoles) {
+ VersionedData data = null;
+ try {
+ data = solrCloudManager.getDistribStateManager()
+ .getData(ZkStateReader.NODE_ROLES + "/" + node);
+ } catch (NoSuchElementException e) {
+ //this node probably went down
+ continue;
+ }
+ if(data != null && data.getData() != null && data.getData().length >0) {
+ @SuppressWarnings("unchecked")
+ Map<String,Object> map = (Map<String, Object>) Utils.fromJSON(data.getData());
+ if(Boolean.FALSE.equals(map.get("hasData"))) {
+ if(liveNodesCopy == liveNodes) {
+ //the map provided should not be modified. So we make a copy
+ liveNodesCopy = new HashSet<>(liveNodes);
+ }
+ liveNodesCopy.remove(node);
+ }
+ }
+ }
+ }
+
+ } catch (NoSuchElementException nsee) {
+ //no problem
+ } catch (Exception e) {
+ throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unable to communicate with Zookeeper");
+ }
+ return liveNodesCopy;
+ }
+
static class NodeImpl implements Node {
public final String nodeName;
diff --git a/solr/core/src/java/org/apache/solr/core/NodeRole.java b/solr/core/src/java/org/apache/solr/core/NodeRole.java
index 1de7832..7feb742 100644
--- a/solr/core/src/java/org/apache/solr/core/NodeRole.java
+++ b/solr/core/src/java/org/apache/solr/core/NodeRole.java
@@ -16,19 +16,18 @@
*/
package org.apache.solr.core;
-import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
import java.util.*;
+import org.apache.solr.common.MapWriter;
import org.apache.solr.common.StringUtils;
import org.apache.solr.common.util.StrUtils;
-public class NodeRole {
- private boolean hasData;
- private Type role;
+public class NodeRole implements MapWriter {
+ private boolean hasData = true;
+ private Type role =Type.data;
public NodeRole(String role) {
if (StringUtils.isEmpty(role)) {
- hasData = true;
- this.role = Type.data;
return;
}
Set<String> roles = new HashSet<>(StrUtils.split(role, ','));
@@ -58,65 +57,18 @@ public class NodeRole {
return role;
}
- @SuppressWarnings("unchecked")
- public Map<String, Object> modifyRoleData(Map<String, Object> rolesData, String nodeName) {
- if (this.role == Type.data) {
- //this is a normal data node, nothing to do
- if (rolesData == null) return null;// no role info, return
- if (removeStaleRoles(nodeName, rolesData, Collections.emptySet())) {
- return rolesData;
- } else {
- return null;
- }
- } else {
- if (rolesData == null) rolesData = new HashMap<>();
- List<String> nodes = (List<String>) rolesData.computeIfAbsent(role.name(), s -> new ArrayList<String>());
- boolean isModified = false;
- if (!nodes.contains(nodeName)) {
- nodes.add(nodeName);
- isModified = true;
- }
- List<String> nonReplicaNodes = (List<String>) rolesData.computeIfAbsent(NO_REPLICAS, s -> new ArrayList<String>());
- if (hasData) {
- if(nonReplicaNodes.contains(nodeName)) {
- nonReplicaNodes.remove(nodeName);
- isModified = true;
- }
- } else {
- if(!nonReplicaNodes.contains(nodeName)){
- nonReplicaNodes.add(nodeName);
- isModified = true;
- }
- }
- if (isModified || removeStaleRoles(nodeName, rolesData, ImmutableSet.of(role.name(), NO_REPLICAS))) {
- return rolesData;
- } else {
- return null;
- }
- }
-
- }
-
- @SuppressWarnings("unchecked")
- private boolean removeStaleRoles(String nodeName, Map<String, Object> rolesData, Set<String> ignore) {
- boolean[] isModified = new boolean[]{false};
- rolesData.forEach((s, o) -> {
- if (ignore.contains(s)) return;
- if (o instanceof List) {
- List<String> list = (List<String>) o;
- if (list.contains(nodeName)) {
- isModified[0] = true;
- list.remove(nodeName);
- }
- }
- });
- return isModified[0];
+ @Override
+ public void writeMap(EntryWriter ew) throws IOException {
+ ew.put("hasData", hasData);
+ ew.put("role", role.name());
}
public enum Type {
data, overseer;
}
+
+
public static final String NO_REPLICAS = "no-replicas";
}
diff --git a/solr/core/src/test/org/apache/solr/core/TestNodeRole.java b/solr/core/src/test/org/apache/solr/core/TestNodeRole.java
deleted file mode 100644
index b328983..0000000
--- a/solr/core/src/test/org/apache/solr/core/TestNodeRole.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF 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.
- */
-package org.apache.solr.core;
-
-import java.lang.invoke.MethodHandles;
-import java.util.Collection;
-import java.util.Map;
-import org.apache.solr.SolrTestCase;
-import org.apache.solr.common.util.Utils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class TestNodeRole extends SolrTestCase {
- private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
-
-
- @SuppressWarnings("rawtypes")
- public void testZkData() {
- //empty
- Map<String,Object> rolesData = null;
- //start a node with overseer role
- rolesData = new NodeRole("overseer").modifyRoleData(rolesData, "node1");
- assertEquals("node1", _val(rolesData, "overseer[0]"));
- assertEquals("node1", _val(rolesData, "no-replicas[0]"));
- //now start another node with overseer
- rolesData = new NodeRole("overseer").modifyRoleData(rolesData, "node2");
- assertEquals("node2", _val(rolesData, "overseer[1]"));
- assertEquals("node2", _val(rolesData, "no-replicas[1]"));
- //now start another node with overseer,data
- rolesData = new NodeRole("overseer,data").modifyRoleData(rolesData, "node3");
-
- assertEquals("node3", _val(rolesData, "overseer[2]"));
- assertEquals(2, ((Collection) _val(rolesData, "no-replicas")).size() );
- //now restart node2 with no role
-
- rolesData = new NodeRole(null).modifyRoleData(rolesData, "node2");
- assertEquals("node3", _val(rolesData, "overseer[1]"));
- assertEquals(1, ((Collection) _val(rolesData, "no-replicas")).size() );
- //now restart node1 with the original values
- assertNull(new NodeRole("overseer").modifyRoleData(rolesData, "node1"));
-
- assertEquals("node1", _val(rolesData, "overseer[0]"));
- assertEquals("node1", _val(rolesData, "no-replicas[0]"));
- //now restart node1 with overseer,data
- rolesData = new NodeRole("overseer,data").modifyRoleData(rolesData, "node1");
- assertEquals("node1", _val(rolesData, "overseer[0]"));
- assertEquals(0, ((Collection) _val(rolesData, "no-replicas")).size() );
-
- log.info("rolesdata : {}", Utils.toJSONString(rolesData));
- }
-
- private Object _val(Map<String, Object> rolesData, String path) {
- return Utils.getObjectByPath(rolesData, false, path);
- }
-}
diff --git a/solr/solrj/src/java/org/apache/solr/common/cloud/ZkStateReader.java b/solr/solrj/src/java/org/apache/solr/common/cloud/ZkStateReader.java
index 461dbaf..a01cab2 100644
--- a/solr/solrj/src/java/org/apache/solr/common/cloud/ZkStateReader.java
+++ b/solr/solrj/src/java/org/apache/solr/common/cloud/ZkStateReader.java
@@ -103,6 +103,7 @@ public class ZkStateReader implements SolrCloseable {
public static final String STATE_TIMESTAMP_PROP = "stateTimestamp";
public static final String COLLECTIONS_ZKNODE = "/collections";
public static final String LIVE_NODES_ZKNODE = "/live_nodes";
+ public static final String NODE_ROLES = "/node_roles";
public static final String ALIASES = "/aliases.json";
/**
* This ZooKeeper file is no longer used starting with Solr 9 but keeping the name around to check if it
[solr] 13/28: Adding defaultIfAbsent, minor formatting fixes
Posted by is...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
ishan pushed a commit to branch jira/SOLR15694
in repository https://gitbox.apache.org/repos/asf/solr.git
commit 072f65909e7fc1e9bde4500af537b1e54ff7c2aa
Author: Ishan Chattopadhyaya <is...@apache.org>
AuthorDate: Fri Dec 10 13:28:45 2021 +0530
Adding defaultIfAbsent, minor formatting fixes
---
.../java/org/apache/solr/cloud/ZkController.java | 4 +-
.../java/org/apache/solr/core/CoreContainer.java | 2 +-
.../src/java/org/apache/solr/core/NodeRoles.java | 89 +++++++++++++---------
.../test/org/apache/solr/cloud/NodeRolesTest.java | 9 +--
4 files changed, 56 insertions(+), 48 deletions(-)
diff --git a/solr/core/src/java/org/apache/solr/cloud/ZkController.java b/solr/core/src/java/org/apache/solr/cloud/ZkController.java
index e7b15bf..5e71098 100644
--- a/solr/core/src/java/org/apache/solr/cloud/ZkController.java
+++ b/solr/core/src/java/org/apache/solr/cloud/ZkController.java
@@ -852,7 +852,7 @@ public class ZkController implements Closeable {
cmdExecutor.ensureExists(ZkStateReader.NODE_ROLES, zkClient);
for (NodeRoles.Role role : NodeRoles.Role.values()) {
cmdExecutor.ensureExists(ZkStateReader.NODE_ROLES + "/" + role.roleName, zkClient);
- for (String v : role.supportedVals()) {
+ for (String v : role.supportedModes()) {
cmdExecutor.ensureExists(ZkStateReader.NODE_ROLES + "/" + role.roleName + "/"+v, zkClient);
}
}
@@ -1091,7 +1091,7 @@ public class ZkController implements Closeable {
ops.add(Op.create(nodePath, null, zkClient.getZkACLProvider().getACLsToAdd(nodePath), CreateMode.EPHEMERAL));
// Create the roles node as well
- cc.nodeRoles.ROLES.forEach((role, val) -> ops.add(Op.create(ZkStateReader.NODE_ROLES + "/" + role.roleName+ "/"+val +"/"+ nodeName,
+ cc.nodeRoles.getRoles().forEach((role, val) -> ops.add(Op.create(ZkStateReader.NODE_ROLES + "/" + role.roleName+ "/"+val +"/"+ nodeName,
null, zkClient.getZkACLProvider().getACLsToAdd(nodePath), CreateMode.EPHEMERAL)));
zkClient.multi(ops, true);
diff --git a/solr/core/src/java/org/apache/solr/core/CoreContainer.java b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
index f7e9a1f..72a0df3 100644
--- a/solr/core/src/java/org/apache/solr/core/CoreContainer.java
+++ b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
@@ -947,7 +947,7 @@ public class CoreContainer {
});
clusterSingletons.setReady();
- if (NodeRoles.PREFERRED.equals(nodeRoles.getRoleVal(NodeRoles.Role.OVERSEER))) {
+ if (NodeRoles.PREFERRED.equals(nodeRoles.getRoleMode(NodeRoles.Role.OVERSEER))) {
try {
log.info("This node is started as a preferred overseer");
zkSys.getZkController().setPreferredOverseer();
diff --git a/solr/core/src/java/org/apache/solr/core/NodeRoles.java b/solr/core/src/java/org/apache/solr/core/NodeRoles.java
index fb3f011..b72e5ec 100644
--- a/solr/core/src/java/org/apache/solr/core/NodeRoles.java
+++ b/solr/core/src/java/org/apache/solr/core/NodeRoles.java
@@ -30,46 +30,65 @@ public class NodeRoles implements MapWriter {
public static final String NODE_ROLES_PROP = "solr.node.roles";
- public Map<Role, String> ROLES ;
+ public static final String ON = "on";
+ public static final String OFF = "off";
+ public static final String ALLOWED = "allowed";
+ public static final String DISALLOWED = "disallowed";
+ public static final String PREFERRED = "preferred";
- public NodeRoles(String role) {
- ROLES = new EnumMap<>(Role.class);
- if (StringUtils.isEmpty(role)) {
- role = DEFAULT_ROLE;
+ public static final String DEFAULT_ROLES_STRING = "data:on,overseer:allowed";
+
+ // Map of roles to mode that are applicable for this node.
+ private Map<Role, String> nodeRoles;
+
+ public NodeRoles(String rolesString) {
+ Map<Role, String> roles = new EnumMap<>(Role.class);
+ if (StringUtils.isEmpty(rolesString)) {
+ rolesString = DEFAULT_ROLES_STRING;
}
- List<String> rolesList = StrUtils.splitSmart(role, ',');
- for (String s : rolesList) {
- List<String> roleVal = StrUtils.splitSmart(s,':');
- Role r = Role.getRole(roleVal.get(0));
- if(r.supportedVals().contains(roleVal.get(1))) {
- ROLES.put(r, roleVal.get(1));
+ List<String> rolesList = StrUtils.splitSmart(rolesString, ',');
+ for (String s: rolesList) {
+ List<String> roleMode = StrUtils.splitSmart(s,':');
+ Role r = Role.getRole(roleMode.get(0));
+ if (r.supportedModes().contains(roleMode.get(1))) {
+ roles.put(r, roleMode.get(1));
} else {
- throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "UNKNOWN role value :"+roleVal.get(0));
+ throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown role mode: " + roleMode.get(0));
}
}
- ROLES = Collections.unmodifiableMap(ROLES);
+ for(Role r: Role.values()) {
+ if (!roles.containsKey(r)) {
+ roles.put(r, r.defaultIfAbsent());
+ }
+ }
+ nodeRoles = Collections.unmodifiableMap(roles);
}
- public String getRoleVal(Role role) {
- return ROLES.get(role);
+ public Map<Role, String> getRoles() {
+ return nodeRoles;
}
-
+ public String getRoleMode(Role role) {
+ return nodeRoles.get(role);
+ }
@Override
public void writeMap(EntryWriter ew) {
- ROLES.forEach((role, s) -> ew.putNoEx(role.roleName, s));
+ nodeRoles.forEach((role, s) -> ew.putNoEx(role.roleName, s));
}
public enum Role {
- DATA("data") ,
+ DATA("data"),
OVERSEER("overseer") {
@Override
- public Set<String> supportedVals() {
- return OVERSEER_VALS;
+ public Set<String> supportedModes() {
+ return Set.of(ALLOWED, PREFERRED, DISALLOWED);
+ }
+ @Override
+ public String defaultIfAbsent() {
+ return DISALLOWED;
}
- },
- COORDINATOR("coordinator");
+ };
public final String roleName;
@@ -78,16 +97,21 @@ public class NodeRoles implements MapWriter {
}
public static Role getRole(String value) {
- // Given a user string "overseer", convert to OVERSEER and return the enum value
- for (Role role : Role.values()) {
- if(value.equals(role.roleName)) return role;
+ for (Role role: Role.values()) {
+ if (value.equals(role.roleName)) return role;
}
- throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown role : "+value);
+ throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown role: " + value);
}
- public Set<String> supportedVals() {
- return ON_OFF;
+ public Set<String> supportedModes() {
+ return Set.of(ON, OFF);
+ }
+ /**
+ * Default mode for a role in nodes where this role is not specified.
+ */
+ public String defaultIfAbsent() {
+ return OFF;
}
@Override
@@ -95,13 +119,4 @@ public class NodeRoles implements MapWriter {
return super.toString().toLowerCase();
}
}
-
- public static final String ON = "on";
- public static final String OFF = "off";
- public static final Set<String> ON_OFF = Set.of(ON,OFF);
- public static final String ALLOWED = "allowed";
- public static final String DISALLOWED = "disallowed";
- public static final String PREFERRED = "preferred";
- public static final Set<String> OVERSEER_VALS = Set.of(ALLOWED, DISALLOWED, PREFERRED);
- public static final String DEFAULT_ROLE = "data:on,overseer:allowed";
}
diff --git a/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java b/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java
index 18a16fe..a2e3651 100644
--- a/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java
@@ -48,7 +48,6 @@ public class NodeRolesTest extends SolrCloudTestCase {
@SuppressWarnings("unchecked")
public void testRoleIntegration() throws Exception {
-
JettySolrRunner j0 = cluster.getJettySolrRunner(0);
JettySolrRunner j1 = null, j2 = null;
j1 = startNodeWithRoles("overseer:preferred,data:off");
@@ -58,19 +57,15 @@ public class NodeRolesTest extends SolrCloudTestCase {
assertEquals(j1.getNodeName(), rsp._getStr("node-roles/data/off[0]", null));
OverseerRolesTest.waitForNewOverseer(20, j1.getNodeName(), false);
- //start another node that is overseer but has data
+ // start another node that is overseer but has data
j2 = startNodeWithRoles("overseer:preferred,data:on");
rsp = new V2Request.Builder("/cluster/node-roles").GET().build().process(cluster.getSolrClient());
-
assertTrue( ((Collection)rsp._get("node-roles/overseer/preferred", Collections.emptyList())).contains(j2.getNodeName()));
assertTrue( ((Collection)rsp._get("node-roles/data/on", Collections.emptyList())).contains(j2.getNodeName()));
-
-
rsp = new V2Request.Builder("/cluster/node-roles/overseer").GET().build().process(cluster.getSolrClient());
-
assertTrue( ((Collection)rsp._get("overseer/preferred", Collections.emptyList())).contains(j2.getNodeName()));
assertTrue( ((Collection)rsp._get("overseer/preferred", Collections.emptyList())).contains(j1.getNodeName()));
@@ -78,8 +73,6 @@ public class NodeRolesTest extends SolrCloudTestCase {
assertTrue( ((Collection)rsp._get("overseer/preferred", Collections.emptyList())).contains(j2.getNodeName()));
assertTrue( ((Collection)rsp._get("overseer/preferred", Collections.emptyList())).contains(j1.getNodeName()));
-
-
String COLLECTION_NAME = "TEST_ROLES";
CollectionAdminRequest
.createCollection(COLLECTION_NAME, "conf", 3, 1)
[solr] 06/28: added APIs
Posted by is...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
ishan pushed a commit to branch jira/SOLR15694
in repository https://gitbox.apache.org/repos/asf/solr.git
commit 75d841055857c49aad8db976919b56da9a6c6a35
Author: Noble Paul <no...@gmail.com>
AuthorDate: Wed Nov 17 13:39:38 2021 +1100
added APIs
---
.../impl/SimpleClusterAbstractionsImpl.java | 45 ++++++++--------------
.../java/org/apache/solr/handler/ClusterAPI.java | 40 ++++++++++++++++++-
.../client/solrj/cloud/DistribStateManager.java | 23 +++++++++++
3 files changed, 78 insertions(+), 30 deletions(-)
diff --git a/solr/core/src/java/org/apache/solr/cluster/placement/impl/SimpleClusterAbstractionsImpl.java b/solr/core/src/java/org/apache/solr/cluster/placement/impl/SimpleClusterAbstractionsImpl.java
index f403aed..16ce53c 100644
--- a/solr/core/src/java/org/apache/solr/cluster/placement/impl/SimpleClusterAbstractionsImpl.java
+++ b/solr/core/src/java/org/apache/solr/cluster/placement/impl/SimpleClusterAbstractionsImpl.java
@@ -19,6 +19,8 @@ package org.apache.solr.cluster.placement.impl;
import java.io.IOException;
import java.util.*;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.BiConsumer;
import java.util.stream.Collectors;
import com.google.common.collect.Maps;
@@ -90,39 +92,26 @@ class SimpleClusterAbstractionsImpl {
private static Set<String> filterNonDataNodes(SolrCloudManager solrCloudManager) {
Set<String> liveNodes = solrCloudManager.getClusterStateProvider().getLiveNodes();
- Set<String> liveNodesCopy = liveNodes;
+ AtomicReference<Set<String>> liveNodesCopy = new AtomicReference<>(liveNodes);
try {
- List<String> nodesWithRoles = solrCloudManager.getDistribStateManager().listData(ZkStateReader.NODE_ROLES);
- if(!nodesWithRoles.isEmpty()) {
- for (String node : nodesWithRoles) {
- VersionedData data = null;
- try {
- data = solrCloudManager.getDistribStateManager()
- .getData(ZkStateReader.NODE_ROLES + "/" + node);
- } catch (NoSuchElementException e) {
- //this node probably went down
- continue;
- }
- if(data != null && data.getData() != null && data.getData().length >0) {
- @SuppressWarnings("unchecked")
- Map<String,Object> map = (Map<String, Object>) Utils.fromJSON(data.getData());
- if(Boolean.FALSE.equals(map.get("hasData"))) {
- if(liveNodesCopy == liveNodes) {
- //the map provided should not be modified. So we make a copy
- liveNodesCopy = new HashSet<>(liveNodes);
- }
- liveNodesCopy.remove(node);
- }
- }
- }
- }
+ solrCloudManager.getDistribStateManager().forEachChild(ZkStateReader.NODE_ROLES, (name, data) -> {
+ if(data != null && data.getData() != null && data.getData().length >0) {
+ @SuppressWarnings("unchecked")
+ Map<String,Object> map = (Map<String, Object>) Utils.fromJSON(data.getData());
+ if(Boolean.FALSE.equals(map.get("hasData"))) {
+ if(liveNodesCopy.get() == liveNodes) {
+ //the map provided should not be modified. So we make a copy
+ liveNodesCopy.set(new HashSet<>(liveNodes));
+ }
+ liveNodesCopy.get().remove(name);
+ }
+ }
+ });
- } catch (NoSuchElementException nsee) {
- //no problem
} catch (Exception e) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unable to communicate with Zookeeper");
}
- return liveNodesCopy;
+ return liveNodesCopy.get();
}
diff --git a/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java b/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
index be5c7de..6f6773d 100644
--- a/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
+++ b/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
@@ -17,8 +17,7 @@
package org.apache.solr.handler;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.*;
import com.google.common.collect.Maps;
import org.apache.solr.api.Command;
@@ -31,6 +30,7 @@ import org.apache.solr.cloud.ConfigSetCmds;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.annotation.JsonProperty;
import org.apache.solr.common.cloud.ClusterProperties;
+import org.apache.solr.common.cloud.ZkStateReader;
import org.apache.solr.common.params.CollectionParams;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.ConfigSetParams;
@@ -73,6 +73,42 @@ public class ClusterAPI {
}
@EndPoint(method = GET,
+ path = "/cluster/node-roles",
+ permission = COLL_READ_PERM)
+ public void roles(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
+ Map <String, Map<String,Object>> result = new LinkedHashMap<>();
+ collectionsHandler.getCoreContainer().getZkController().getSolrCloudManager().getDistribStateManager().
+ forEachChild(ZkStateReader.NODE_ROLES, (node, data) -> {
+ if(data != null && data.getData() != null) {
+ result.put(node, (Map<String, Object>) Utils.fromJSON(data.getData()));
+ }
+ });
+ rsp.add("node-roles", result);
+ }
+
+ @EndPoint(method = GET,
+ path = "/cluster/node-roles/{role}",
+ permission = COLL_READ_PERM)
+ public void rolesOfNode(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
+ String role = req.getPathTemplateValues().get("role");
+
+ List<String> result = new ArrayList<>();
+ collectionsHandler.getCoreContainer().getZkController().getSolrCloudManager().getDistribStateManager().
+ forEachChild(ZkStateReader.NODE_ROLES, (node, data) -> {
+
+ if(data != null && data.getData() != null) {
+ @SuppressWarnings("unchecked")
+ Map<String, Object> roleData = (Map<String, Object>) Utils.fromJSON(data.getData());
+ if(role.equals(roleData.get("role"))) {
+ result.add(node);
+ }
+ }
+ });
+ rsp.add("nodes", result);
+ }
+
+
+ @EndPoint(method = GET,
path = "/cluster/aliases",
permission = COLL_READ_PERM)
public void aliases(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/DistribStateManager.java b/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/DistribStateManager.java
index feae27f..ebd4db7 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/DistribStateManager.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/DistribStateManager.java
@@ -24,8 +24,10 @@ import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;
+import java.util.function.BiConsumer;
import org.apache.solr.common.SolrCloseable;
import org.apache.solr.common.cloud.PerReplicaStates;
+import org.apache.solr.common.cloud.ZkStateReader;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.Op;
@@ -141,4 +143,25 @@ public interface DistribStateManager extends SolrCloseable {
}
}
}
+ default void forEachChild(String path, BiConsumer<String, VersionedData> fun)
+ throws InterruptedException, IOException, KeeperException {
+ List<String> children = null;
+ try {
+ children = listData(ZkStateReader.NODE_ROLES);
+ } catch (NoSuchElementException e) {
+ return;
+ }
+ if(!children.isEmpty()) {
+ for (String node : children) {
+ VersionedData data = null;
+ try {
+ data = getData(ZkStateReader.NODE_ROLES + "/" + node);
+ } catch (NoSuchElementException e) {
+ //this node probably was deleted
+ continue;
+ }
+ fun.accept(node, data);
+ }
+ }
+ }
}
[solr] 20/28: removed unnecessary changes
Posted by is...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
ishan pushed a commit to branch jira/SOLR15694
in repository https://gitbox.apache.org/repos/asf/solr.git
commit 37b216c5a3df9d881617c6bda5b18ebe63ce1032
Author: Noble Paul <no...@gmail.com>
AuthorDate: Fri Dec 17 16:43:39 2021 +1100
removed unnecessary changes
---
solr/core/src/java/org/apache/solr/cloud/ZkController.java | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/solr/core/src/java/org/apache/solr/cloud/ZkController.java b/solr/core/src/java/org/apache/solr/cloud/ZkController.java
index a249e4f..ee0c87f 100644
--- a/solr/core/src/java/org/apache/solr/cloud/ZkController.java
+++ b/solr/core/src/java/org/apache/solr/cloud/ZkController.java
@@ -73,7 +73,13 @@ import org.apache.solr.common.util.SolrNamedThreadFactory;
import org.apache.solr.common.util.StrUtils;
import org.apache.solr.common.util.URLUtil;
import org.apache.solr.common.util.Utils;
-import org.apache.solr.core.*;
+import org.apache.solr.core.CloseHook;
+import org.apache.solr.core.CloudConfig;
+import org.apache.solr.core.CoreContainer;
+import org.apache.solr.core.CoreDescriptor;
+import org.apache.solr.core.SolrCore;
+import org.apache.solr.core.NodeRoles;
+import org.apache.solr.core.SolrCoreInitializationException;
import org.apache.solr.handler.component.HttpShardHandler;
import org.apache.solr.logging.MDCLoggingContext;
import org.apache.solr.search.SolrIndexSearcher;
[solr] 17/28: removed nocommit
Posted by is...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
ishan pushed a commit to branch jira/SOLR15694
in repository https://gitbox.apache.org/repos/asf/solr.git
commit 4fb3713a7f611a5d92408017530606d080fe44a1
Author: Noble Paul <no...@gmail.com>
AuthorDate: Fri Dec 17 16:36:14 2021 +1100
removed nocommit
---
solr/core/src/java/org/apache/solr/handler/ClusterAPI.java | 1 -
1 file changed, 1 deletion(-)
diff --git a/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java b/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
index 446cd9b..9f93c39 100644
--- a/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
+++ b/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
@@ -84,7 +84,6 @@ public class ClusterAPI {
@EndPoint(method = GET,
path = "/cluster/node-roles",
permission = COLL_READ_PERM)
- // nocommit: it must also output all data nodes that didn't start with -Dsolr.node.roles
public void roles(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
Map<String, Object> result = new LinkedHashMap<>();