From 6f90a35252f8e84f742a71f336d3c5b642621746 Mon Sep 17 00:00:00 2001 From: jaykay12 Date: Fri, 30 Oct 2026 17:12:52 +0530 Subject: [PATCH 01/15] added cl --- .../SOLR-16398-disable-enable-poll.yml | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 changelog/unreleased/SOLR-16398-disable-enable-poll.yml diff --git a/changelog/unreleased/SOLR-16398-disable-enable-poll.yml b/changelog/unreleased/SOLR-16398-disable-enable-poll.yml new file mode 100644 index 00000000000..85a30b48933 --- /dev/null +++ b/changelog/unreleased/SOLR-16398-disable-enable-poll.yml @@ -0,0 +1,32 @@ +# (DELETE ALL COMMENTS UP HERE AFTER FILLING THIS IN + +# See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc + +# If the change is minor, don't bother adding a changelog entry. +# For `other` type entries, the threshold to bother with a changelog entry should be even higher. + +# title: +# * The audience is end-users and administrators, not committers. +# * Be short and focused on the user impact. Multiple sentences is fine! +# * For technical/geeky details, prefer the commit message instead of changelog. +# * Reference JIRA issues like `SOLR-12345`, or if no JIRA but have a GitHub PR then `PR#12345`. + +# type: +# `added` for new features/improvements, opt-in by the user typically documented in the ref guide +# `changed` for improvements; not opt-in +# `fixed` for improvements that are deemed to have fixed buggy behavior +# `deprecated` for marking things deprecated +# `removed` for code removed +# `dependency_update` for updates to dependencies +# `other` for anything else, like large/significant refactorings, build changes, +# test infrastructure, or documentation. +# Most such changes are too small/minor to bother with a changelog entry. + +title: > + Disable enable poll +type: +authors: + - name: jaykay12 +links: + - name: SOLR-16398 + url: https://issues.apache.org/jira/browse/SOLR-16398 From e3b2924c7d5c55ca0df019a87307b96372cf173e Mon Sep 17 00:00:00 2001 From: jaykay12 Date: Fri, 30 Oct 2026 19:43:08 +0530 Subject: [PATCH 02/15] initial changes --- .../SOLR-16398-disable-enable-poll.yml | 33 +++-------------- .../client/api/endpoint/ReplicationApis.java | 10 ++++++ .../model/ReplicationPollingRequestBody.java | 26 ++++++++++++++ .../solr/handler/ReplicationHandler.java | 35 ++++++++----------- .../handler/admin/api/CoreReplication.java | 6 ++++ .../handler/admin/api/ReplicationAPIBase.java | 11 ++++++ 6 files changed, 73 insertions(+), 48 deletions(-) create mode 100644 solr/api/src/java/org/apache/solr/client/api/model/ReplicationPollingRequestBody.java diff --git a/changelog/unreleased/SOLR-16398-disable-enable-poll.yml b/changelog/unreleased/SOLR-16398-disable-enable-poll.yml index 85a30b48933..5449eda1ff5 100644 --- a/changelog/unreleased/SOLR-16398-disable-enable-poll.yml +++ b/changelog/unreleased/SOLR-16398-disable-enable-poll.yml @@ -1,32 +1,9 @@ -# (DELETE ALL COMMENTS UP HERE AFTER FILLING THIS IN - -# See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc - -# If the change is minor, don't bother adding a changelog entry. -# For `other` type entries, the threshold to bother with a changelog entry should be even higher. - -# title: -# * The audience is end-users and administrators, not committers. -# * Be short and focused on the user impact. Multiple sentences is fine! -# * For technical/geeky details, prefer the commit message instead of changelog. -# * Reference JIRA issues like `SOLR-12345`, or if no JIRA but have a GitHub PR then `PR#12345`. - -# type: -# `added` for new features/improvements, opt-in by the user typically documented in the ref guide -# `changed` for improvements; not opt-in -# `fixed` for improvements that are deemed to have fixed buggy behavior -# `deprecated` for marking things deprecated -# `removed` for code removed -# `dependency_update` for updates to dependencies -# `other` for anything else, like large/significant refactorings, build changes, -# test infrastructure, or documentation. -# Most such changes are too small/minor to bother with a changelog entry. - -title: > - Disable enable poll -type: +title: + Added V2 APIs to enable and disable polling of a leader from its follower +type: added authors: - - name: jaykay12 + - name: Jalaz Kumar + nick: jaykay12 links: - name: SOLR-16398 url: https://issues.apache.org/jira/browse/SOLR-16398 diff --git a/solr/api/src/java/org/apache/solr/client/api/endpoint/ReplicationApis.java b/solr/api/src/java/org/apache/solr/client/api/endpoint/ReplicationApis.java index 3457528fa2f..27f7c42621a 100644 --- a/solr/api/src/java/org/apache/solr/client/api/endpoint/ReplicationApis.java +++ b/solr/api/src/java/org/apache/solr/client/api/endpoint/ReplicationApis.java @@ -36,6 +36,7 @@ import org.apache.solr.client.api.model.IndexVersionResponse; import org.apache.solr.client.api.model.ReplicationDetailsResponse; import org.apache.solr.client.api.model.ReplicationEnabledRequestBody; +import org.apache.solr.client.api.model.ReplicationPollingRequestBody; import org.apache.solr.client.api.model.RestoreStatusResponse; import org.apache.solr.client.api.model.SolrJerseyResponse; import org.apache.solr.client.api.util.CoreApiParameters; @@ -136,4 +137,13 @@ StreamingOutput fetchFile( tags = {"replication"}) SolrJerseyResponse setReplicationEnabled( @RequestBody(required = true) ReplicationEnabledRequestBody requestBody); + + @PUT + @CoreApiParameters + @Path("/polling") + @Operation( + summary = "Enable or disable polling of the leader by a follower.", + tags = {"replication"}) + SolrJerseyResponse setReplicationPolling( + @RequestBody(required = true) ReplicationPollingRequestBody requestBody); } diff --git a/solr/api/src/java/org/apache/solr/client/api/model/ReplicationPollingRequestBody.java b/solr/api/src/java/org/apache/solr/client/api/model/ReplicationPollingRequestBody.java new file mode 100644 index 00000000000..8d550206a3a --- /dev/null +++ b/solr/api/src/java/org/apache/solr/client/api/model/ReplicationPollingRequestBody.java @@ -0,0 +1,26 @@ +/* + * 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.client.api.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** Request body for enabling and disabling polling of the leader by a follower. */ +public class ReplicationPollingRequestBody { + + @JsonProperty(required = true) + public Boolean enabled; +} diff --git a/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java b/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java index 3196641a0f6..ab8a9e7b67a 100644 --- a/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java @@ -331,9 +331,17 @@ public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throw } else if (command.equalsIgnoreCase(CMD_FETCH_INDEX)) { fetchIndex(solrParams, rsp); } else if (command.equalsIgnoreCase(CMD_DISABLE_POLL)) { - disablePoll(rsp); + if (setPollingEnabled(false)) { + rsp.add(STATUS, OK_STATUS); + } else { + reportErrorOnResponse(rsp, "No follower configured", null); + } } else if (command.equalsIgnoreCase(CMD_ENABLE_POLL)) { - enablePoll(rsp); + if (setPollingEnabled(true)) { + rsp.add(STATUS, OK_STATUS); + } else { + reportErrorOnResponse(rsp, "No follower configured", null); + } } else if (command.equalsIgnoreCase(CMD_ABORT_FETCH)) { if (abortFetch()) { rsp.add(STATUS, OK_STATUS); @@ -852,24 +860,11 @@ public FileInfo(long lasmodified, String name, long size, long checksum) { } } - private void disablePoll(SolrQueryResponse rsp) { - if (pollingIndexFetcher != null) { - pollDisabled.set(true); - log.info("inside disable poll, value of pollDisabled = {}", pollDisabled); - rsp.add(STATUS, OK_STATUS); - } else { - reportErrorOnResponse(rsp, "No follower configured", null); - } - } - - private void enablePoll(SolrQueryResponse rsp) { - if (pollingIndexFetcher != null) { - pollDisabled.set(false); - log.info("inside enable poll, value of pollDisabled = {}", pollDisabled); - rsp.add(STATUS, OK_STATUS); - } else { - reportErrorOnResponse(rsp, "No follower configured", null); - } + public boolean setPollingEnabled(boolean enabled) { + if (pollingIndexFetcher == null) return false; + pollDisabled.set(!enabled); + log.info("polling enabled = {}", enabled); + return true; } boolean isPollingDisabled() { diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/CoreReplication.java b/solr/core/src/java/org/apache/solr/handler/admin/api/CoreReplication.java index ea5de6768ed..e1b786bc7a5 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/api/CoreReplication.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/api/CoreReplication.java @@ -28,6 +28,7 @@ import org.apache.solr.client.api.model.IndexVersionResponse; import org.apache.solr.client.api.model.ReplicationDetailsResponse; import org.apache.solr.client.api.model.ReplicationEnabledRequestBody; +import org.apache.solr.client.api.model.ReplicationPollingRequestBody; import org.apache.solr.client.api.model.RestoreStatusResponse; import org.apache.solr.client.api.model.SolrJerseyResponse; import org.apache.solr.common.SolrException; @@ -103,4 +104,9 @@ public StreamingOutput fetchFile( public SolrJerseyResponse setReplicationEnabled(ReplicationEnabledRequestBody requestBody) { return doSetReplicationEnabled(requestBody); } + + @Override + public SolrJerseyResponse setReplicationPolling(ReplicationPollingRequestBody requestBody) { + return doSetReplicationPolling(requestBody); + } } diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/ReplicationAPIBase.java b/solr/core/src/java/org/apache/solr/handler/admin/api/ReplicationAPIBase.java index 107d8818ecb..afe52f6daa1 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/api/ReplicationAPIBase.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/api/ReplicationAPIBase.java @@ -51,6 +51,7 @@ import org.apache.solr.client.api.model.IndexVersionResponse; import org.apache.solr.client.api.model.ReplicationDetailsResponse; import org.apache.solr.client.api.model.ReplicationEnabledRequestBody; +import org.apache.solr.client.api.model.ReplicationPollingRequestBody; import org.apache.solr.client.api.model.RestoreStatusResponse; import org.apache.solr.client.api.model.SolrJerseyResponse; import org.apache.solr.common.SolrException; @@ -113,6 +114,16 @@ protected SolrJerseyResponse doSetReplicationEnabled(ReplicationEnabledRequestBo return instantiateJerseyResponse(SolrJerseyResponse.class); } + protected SolrJerseyResponse doSetReplicationPolling(ReplicationPollingRequestBody requestBody) { + ensureRequiredRequestBodyProvided(requestBody); + ensureRequiredParameterProvided("enabled", requestBody.enabled); + ReplicationHandler replicationHandler = + (ReplicationHandler) solrCore.getRequestHandler(ReplicationHandler.PATH); + if (!replicationHandler.setPollingEnabled(requestBody.enabled)) + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "No follower configured"); + return instantiateJerseyResponse(SolrJerseyResponse.class); + } + protected RestoreStatusResponse doFetchRestoreStatus() { ReplicationHandler replicationHandler = (ReplicationHandler) solrCore.getRequestHandler(ReplicationHandler.PATH); From 34d2298332c7f897af83d6ee34bfff7f0c3b65bf Mon Sep 17 00:00:00 2001 From: jaykay12 Date: Fri, 30 Oct 2026 19:51:55 +0530 Subject: [PATCH 03/15] minor --- .../src/java/org/apache/solr/handler/ReplicationHandler.java | 5 +++++ .../org/apache/solr/handler/admin/api/CoreReplication.java | 1 + .../apache/solr/handler/admin/api/ReplicationAPIBase.java | 3 ++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java b/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java index ab8a9e7b67a..241b861ec22 100644 --- a/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java @@ -860,6 +860,11 @@ public FileInfo(long lasmodified, String name, long size, long checksum) { } } + /** + * Enables or disables polling of the leader + * + * @return false if this core is not configured as a follower + */ public boolean setPollingEnabled(boolean enabled) { if (pollingIndexFetcher == null) return false; pollDisabled.set(!enabled); diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/CoreReplication.java b/solr/core/src/java/org/apache/solr/handler/admin/api/CoreReplication.java index e1b786bc7a5..4e55f5ebc44 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/api/CoreReplication.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/api/CoreReplication.java @@ -106,6 +106,7 @@ public SolrJerseyResponse setReplicationEnabled(ReplicationEnabledRequestBody re } @Override + @PermissionName(CORE_EDIT_PERM) public SolrJerseyResponse setReplicationPolling(ReplicationPollingRequestBody requestBody) { return doSetReplicationPolling(requestBody); } diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/ReplicationAPIBase.java b/solr/core/src/java/org/apache/solr/handler/admin/api/ReplicationAPIBase.java index afe52f6daa1..7468d108a4f 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/api/ReplicationAPIBase.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/api/ReplicationAPIBase.java @@ -119,8 +119,9 @@ protected SolrJerseyResponse doSetReplicationPolling(ReplicationPollingRequestBo ensureRequiredParameterProvided("enabled", requestBody.enabled); ReplicationHandler replicationHandler = (ReplicationHandler) solrCore.getRequestHandler(ReplicationHandler.PATH); - if (!replicationHandler.setPollingEnabled(requestBody.enabled)) + if (!replicationHandler.setPollingEnabled(requestBody.enabled)) { throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "No follower configured"); + } return instantiateJerseyResponse(SolrJerseyResponse.class); } From 0e2fa3e2e53d463c1e63d0ffaabaa4502568aab4 Mon Sep 17 00:00:00 2001 From: jaykay12 Date: Sat, 31 Oct 2026 01:25:43 +0530 Subject: [PATCH 04/15] refcatoring --- .../solr/handler/ReplicationHandler.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java b/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java index 241b861ec22..4fc8d084cf3 100644 --- a/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java @@ -331,17 +331,9 @@ public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throw } else if (command.equalsIgnoreCase(CMD_FETCH_INDEX)) { fetchIndex(solrParams, rsp); } else if (command.equalsIgnoreCase(CMD_DISABLE_POLL)) { - if (setPollingEnabled(false)) { - rsp.add(STATUS, OK_STATUS); - } else { - reportErrorOnResponse(rsp, "No follower configured", null); - } + setPollingEnabled(false, rsp); } else if (command.equalsIgnoreCase(CMD_ENABLE_POLL)) { - if (setPollingEnabled(true)) { - rsp.add(STATUS, OK_STATUS); - } else { - reportErrorOnResponse(rsp, "No follower configured", null); - } + setPollingEnabled(true, rsp); } else if (command.equalsIgnoreCase(CMD_ABORT_FETCH)) { if (abortFetch()) { rsp.add(STATUS, OK_STATUS); @@ -861,7 +853,7 @@ public FileInfo(long lasmodified, String name, long size, long checksum) { } /** - * Enables or disables polling of the leader + * Enables or disables polling of the leader. * * @return false if this core is not configured as a follower */ @@ -872,6 +864,14 @@ public boolean setPollingEnabled(boolean enabled) { return true; } + private void setPollingEnabled(boolean enabled, SolrQueryResponse rsp) { + if (setPollingEnabled(enabled)) { + rsp.add(STATUS, OK_STATUS); + } else { + reportErrorOnResponse(rsp, "No follower configured", null); + } + } + boolean isPollingDisabled() { return pollDisabled.get(); } From 588da93fbad378209495cbb8c699fa9989325117 Mon Sep 17 00:00:00 2001 From: jaykay12 Date: Sat, 31 Oct 2026 01:40:42 +0530 Subject: [PATCH 05/15] ref guides --- .../solr/handler/ReplicationHandler.java | 6 +-- .../pages/user-managed-index-replication.adoc | 37 +++++++++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java b/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java index 4fc8d084cf3..6cd37796a99 100644 --- a/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java @@ -331,9 +331,9 @@ public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throw } else if (command.equalsIgnoreCase(CMD_FETCH_INDEX)) { fetchIndex(solrParams, rsp); } else if (command.equalsIgnoreCase(CMD_DISABLE_POLL)) { - setPollingEnabled(false, rsp); + respondWithPollingEnabled(false, rsp); } else if (command.equalsIgnoreCase(CMD_ENABLE_POLL)) { - setPollingEnabled(true, rsp); + respondWithPollingEnabled(true, rsp); } else if (command.equalsIgnoreCase(CMD_ABORT_FETCH)) { if (abortFetch()) { rsp.add(STATUS, OK_STATUS); @@ -864,7 +864,7 @@ public boolean setPollingEnabled(boolean enabled) { return true; } - private void setPollingEnabled(boolean enabled, SolrQueryResponse rsp) { + private void respondWithPollingEnabled(boolean enabled, SolrQueryResponse rsp) { if (setPollingEnabled(enabled)) { rsp.add(STATUS, OK_STATUS); } else { diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/user-managed-index-replication.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/user-managed-index-replication.adoc index 277038c3686..17ab9f4c9c5 100644 --- a/solr/solr-ref-guide/modules/deployment-guide/pages/user-managed-index-replication.adoc +++ b/solr/solr-ref-guide/modules/deployment-guide/pages/user-managed-index-replication.adoc @@ -456,15 +456,52 @@ http://{followerHost}:{port}/solr/{coreName}/replication?command=abortfetch `enablepoll`:: Enable the specified follower to poll for changes on the leader. + +[tabs#enablepoll-request] +====== +V1 API:: ++ +==== [source,bash] +---- http://{followerHost}:{port}/solr/{coreName}/replication?command=enablepoll +---- +==== + +V2 API:: ++ +==== +[source,bash] +---- +curl -X PUT -H 'Content-Type: application/json' -d '{"enabled": true}' \ +http://{followerHost}:{port}/api/cores/{coreName}/replication/polling +---- +==== +====== `disablepoll`:: Disable the specified follower from polling for changes on the leader. + +[tabs#disablepoll-request] +====== +V1 API:: ++ +==== [source,bash] +---- http://{followerHost}:{port}/solr/{coreName}/replication?command=disablepoll +---- +==== +V2 API:: ++ +==== +[source,bash] +---- +curl -X PUT -H 'Content-Type: application/json' -d '{"enabled": false}' \ +http://{followerHost}:{port}/api/cores/{coreName}/replication/polling +---- +==== +====== `commits`:: Retrieve the index commit points currently retained by the core, with each commit's index version, generation, and the file list. + From 565313fcc194803b3b3a515f34c532d3dea8e626 Mon Sep 17 00:00:00 2001 From: jaykay12 Date: Sat, 31 Oct 2026 01:42:08 +0530 Subject: [PATCH 06/15] nit --- .../deployment-guide/pages/user-managed-index-replication.adoc | 1 + 1 file changed, 1 insertion(+) diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/user-managed-index-replication.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/user-managed-index-replication.adoc index 17ab9f4c9c5..785e0dccdb3 100644 --- a/solr/solr-ref-guide/modules/deployment-guide/pages/user-managed-index-replication.adoc +++ b/solr/solr-ref-guide/modules/deployment-guide/pages/user-managed-index-replication.adoc @@ -502,6 +502,7 @@ http://{followerHost}:{port}/api/cores/{coreName}/replication/polling ---- ==== ====== + `commits`:: Retrieve the index commit points currently retained by the core, with each commit's index version, generation, and the file list. + From ad1dfec89ccc5bb2a1fecc48508d9a63af74f4fb Mon Sep 17 00:00:00 2001 From: jaykay12 Date: Sat, 31 Oct 2026 01:57:25 +0530 Subject: [PATCH 07/15] v1 should be using v2 logic --- .../solr/handler/ReplicationHandler.java | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java b/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java index 6cd37796a99..e8f9a144d3b 100644 --- a/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java @@ -83,6 +83,7 @@ import org.apache.solr.client.api.model.ReplicationDetailsResponse.FollowerDetails; import org.apache.solr.client.api.model.ReplicationDetailsResponse.LeaderDetails; import org.apache.solr.client.api.model.ReplicationDetailsResponse.ReplicationDetails; +import org.apache.solr.client.api.model.ReplicationPollingRequestBody; import org.apache.solr.client.api.model.RestoreStatusResponse; import org.apache.solr.client.api.model.RestoreStatusResponse.RestoreState; import org.apache.solr.client.api.model.RestoreStatusResponse.RestoreStatus; @@ -331,9 +332,9 @@ public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throw } else if (command.equalsIgnoreCase(CMD_FETCH_INDEX)) { fetchIndex(solrParams, rsp); } else if (command.equalsIgnoreCase(CMD_DISABLE_POLL)) { - respondWithPollingEnabled(false, rsp); + respondWithPollingEnabled(false, req, rsp); } else if (command.equalsIgnoreCase(CMD_ENABLE_POLL)) { - respondWithPollingEnabled(true, rsp); + respondWithPollingEnabled(true, req, rsp); } else if (command.equalsIgnoreCase(CMD_ABORT_FETCH)) { if (abortFetch()) { rsp.add(STATUS, OK_STATUS); @@ -860,15 +861,19 @@ public FileInfo(long lasmodified, String name, long size, long checksum) { public boolean setPollingEnabled(boolean enabled) { if (pollingIndexFetcher == null) return false; pollDisabled.set(!enabled); - log.info("polling enabled = {}", enabled); + log.info("Polling {} for core {}", enabled ? "enabled" : "disabled", core.getName()); return true; } - private void respondWithPollingEnabled(boolean enabled, SolrQueryResponse rsp) { - if (setPollingEnabled(enabled)) { + private void respondWithPollingEnabled( + boolean enabled, SolrQueryRequest req, SolrQueryResponse rsp) { + final ReplicationPollingRequestBody requestBody = new ReplicationPollingRequestBody(); + requestBody.enabled = enabled; + try { + new CoreReplication(core, req, rsp).setReplicationPolling(requestBody); rsp.add(STATUS, OK_STATUS); - } else { - reportErrorOnResponse(rsp, "No follower configured", null); + } catch (SolrException e) { + reportErrorOnResponse(rsp, e.getMessage(), null); } } From 2cda1c3479b8c0dc2061eb3aad0f84a96281bbe4 Mon Sep 17 00:00:00 2001 From: jaykay12 Date: Sat, 31 Oct 2026 02:01:49 +0530 Subject: [PATCH 08/15] logging removed --- .../src/java/org/apache/solr/handler/ReplicationHandler.java | 1 - 1 file changed, 1 deletion(-) diff --git a/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java b/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java index e8f9a144d3b..549d4daf736 100644 --- a/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java @@ -861,7 +861,6 @@ public FileInfo(long lasmodified, String name, long size, long checksum) { public boolean setPollingEnabled(boolean enabled) { if (pollingIndexFetcher == null) return false; pollDisabled.set(!enabled); - log.info("Polling {} for core {}", enabled ? "enabled" : "disabled", core.getName()); return true; } From 873945b8ad60b44e63e8944bb67a66cdecc0ab50 Mon Sep 17 00:00:00 2001 From: jaykay12 Date: Sat, 31 Oct 2026 22:42:12 +0530 Subject: [PATCH 09/15] minor cl --- changelog/unreleased/SOLR-16398-disable-enable-poll.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/changelog/unreleased/SOLR-16398-disable-enable-poll.yml b/changelog/unreleased/SOLR-16398-disable-enable-poll.yml index 5449eda1ff5..a9c95729cb5 100644 --- a/changelog/unreleased/SOLR-16398-disable-enable-poll.yml +++ b/changelog/unreleased/SOLR-16398-disable-enable-poll.yml @@ -1,5 +1,4 @@ -title: - Added V2 APIs to enable and disable polling of a leader from its follower +title: Added V2 APIs to enable and disable polling of a leader from its follower type: added authors: - name: Jalaz Kumar From 29b0a0c34b0999164f8ed73bdf7450179a229a80 Mon Sep 17 00:00:00 2001 From: jaykay12 Date: Sat, 31 Oct 2026 23:22:15 +0530 Subject: [PATCH 10/15] tests --- .../admin/api/CoreReplicationAPITest.java | 38 ++++++ .../api/SetReplicationPollingAPITest.java | 109 ++++++++++++++++++ 2 files changed, 147 insertions(+) create mode 100644 solr/core/src/test/org/apache/solr/handler/admin/api/SetReplicationPollingAPITest.java diff --git a/solr/core/src/test/org/apache/solr/handler/admin/api/CoreReplicationAPITest.java b/solr/core/src/test/org/apache/solr/handler/admin/api/CoreReplicationAPITest.java index 0cd932f8c4e..b1fab305f3e 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/api/CoreReplicationAPITest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/api/CoreReplicationAPITest.java @@ -18,6 +18,7 @@ package org.apache.solr.handler.admin.api; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import io.opentelemetry.api.trace.Span; @@ -38,6 +39,7 @@ import org.apache.solr.client.api.model.ReplicationDetailsResponse; import org.apache.solr.client.api.model.ReplicationDetailsResponse.ReplicationDetails; import org.apache.solr.client.api.model.ReplicationEnabledRequestBody; +import org.apache.solr.client.api.model.ReplicationPollingRequestBody; import org.apache.solr.client.api.model.RestoreStatusResponse; import org.apache.solr.common.SolrException; import org.apache.solr.core.SolrCore; @@ -129,6 +131,42 @@ public void testSetReplicationEnabledRejectsBadBody() { assertEquals(SolrException.ErrorCode.BAD_REQUEST.code, ex.code()); } + @Test + public void testSetReplicationPolling() { + for (boolean enabled : new boolean[] {true, false}) { + when(mockReplicationHandler.setPollingEnabled(enabled)).thenReturn(true); + final var body = new ReplicationPollingRequestBody(); + body.enabled = enabled; + assertNotNull(coreReplicationAPI.setReplicationPolling(body)); + verify(mockReplicationHandler).setPollingEnabled(enabled); + } + } + + @Test + public void testSetReplicationPollingRejectsNoFollower() { + final var body = new ReplicationPollingRequestBody(); + body.enabled = true; + SolrException ex = + expectThrows(SolrException.class, () -> coreReplicationAPI.setReplicationPolling(body)); + assertEquals(SolrException.ErrorCode.BAD_REQUEST.code, ex.code()); + } + + @Test + public void testSetReplicationPollingRejectsNullBody() { + SolrException ex = + expectThrows(SolrException.class, () -> coreReplicationAPI.setReplicationPolling(null)); + assertEquals(SolrException.ErrorCode.BAD_REQUEST.code, ex.code()); + } + + @Test + public void testSetReplicationPollingRejectsBadBody() { + final var emptyBody = new ReplicationPollingRequestBody(); + SolrException ex = + expectThrows( + SolrException.class, () -> coreReplicationAPI.setReplicationPolling(emptyBody)); + assertEquals(SolrException.ErrorCode.BAD_REQUEST.code, ex.code()); + } + @Test public void testGetRestoreStatus() { RestoreStatusResponse expected = new RestoreStatusResponse(); diff --git a/solr/core/src/test/org/apache/solr/handler/admin/api/SetReplicationPollingAPITest.java b/solr/core/src/test/org/apache/solr/handler/admin/api/SetReplicationPollingAPITest.java new file mode 100644 index 00000000000..38d1f29ed90 --- /dev/null +++ b/solr/core/src/test/org/apache/solr/handler/admin/api/SetReplicationPollingAPITest.java @@ -0,0 +1,109 @@ +package org.apache.solr.handler.admin.api; + +import static org.apache.solr.core.CoreContainer.ALLOW_PATHS_SYSPROP; + +import java.nio.file.Files; +import java.nio.file.Path; +import org.apache.commons.io.file.PathUtils; +import org.apache.solr.SolrTestCase; +import org.apache.solr.client.solrj.RemoteSolrException; +import org.apache.solr.client.solrj.request.ReplicationApi; +import org.apache.solr.common.SolrException; +import org.apache.solr.util.ExternalPaths; +import org.apache.solr.util.SolrJettyTestRule; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; + +public class SetReplicationPollingAPITest extends SolrTestCase { + + private static final String CORE_NAME = "setReplicationPollingApiTestCore"; + private static final String FOLLOWER_CORE_NAME = "setReplicationPollingApiTestFollowerCore"; + + @ClassRule + public static final SolrJettyTestRule solrTestRule = new SolrJettyTestRule(); + + @BeforeClass + public static void setupSolr() throws Exception { + System.setProperty(ALLOW_PATHS_SYSPROP, ExternalPaths.SERVER_HOME.toAbsolutePath().toString()); + System.setProperty("solr.security.allow.urls.enabled", "false"); + final Path solrHome = createTempDir(); + solrTestRule.startSolr(solrHome); + solrTestRule.newCollection(CORE_NAME).withConfigSet(ExternalPaths.DEFAULT_CONFIGSET).create(); + + final Path followerConfigSet = solrHome.resolve("followerConfigSet"); + PathUtils.copyDirectory(ExternalPaths.DEFAULT_CONFIGSET, followerConfigSet); + final Path solrConfig = followerConfigSet.resolve("solrconfig.xml"); + final String followerHandler = + "" + + "" + + solrTestRule.getBaseUrl() + + "/" + + CORE_NAME + + "01:00:00"; + Files.writeString( + solrConfig, Files.readString(solrConfig).replace("", followerHandler)); + solrTestRule.newCollection(FOLLOWER_CORE_NAME).withConfigSet(followerConfigSet).create(); + } + + @Test + public void testDisableThenEnablePolling() throws Exception { + assertEquals(Boolean.FALSE, fetchPollingDisabled()); + + setPolling(false); + assertEquals(Boolean.TRUE, fetchPollingDisabled()); + + setPolling(true); + assertEquals(Boolean.FALSE, fetchPollingDisabled()); + } + + @Test + public void testMissingEnabledIsRejectedAndStateUnchanged() throws Exception { + var request = new ReplicationApi.SetReplicationPolling(FOLLOWER_CORE_NAME); + RemoteSolrException ex = + expectThrows( + RemoteSolrException.class, + () -> request.process(solrTestRule.getSolrClient(FOLLOWER_CORE_NAME))); + assertEquals(SolrException.ErrorCode.BAD_REQUEST.code, ex.code()); + + assertEquals(Boolean.FALSE, fetchPollingDisabled()); + } + + private static void setPolling(boolean enabled) throws Exception { + var request = new ReplicationApi.SetReplicationPolling(FOLLOWER_CORE_NAME); + request.setEnabled(enabled); + var response = request.process(solrTestRule.getSolrClient(FOLLOWER_CORE_NAME)); + assertNull(response.error); + } + + private static Boolean fetchPollingDisabled() throws Exception { + var request = new ReplicationApi.FetchReplicationDetails(FOLLOWER_CORE_NAME); + return request.process(solrTestRule.getSolrClient(FOLLOWER_CORE_NAME)) + .details + .follower + .isPollingDisabled; + } + + @Test + public void testNoFollowerIsRejected() { + var request = new ReplicationApi.SetReplicationPolling(CORE_NAME); + request.setEnabled(true); + RemoteSolrException ex = + expectThrows( + RemoteSolrException.class, + () -> request.process(solrTestRule.getSolrClient(CORE_NAME))); + assertEquals(SolrException.ErrorCode.BAD_REQUEST.code, ex.code()); + } + + @Test + public void testNullEnabledIsRejected() { + var request = new ReplicationApi.SetReplicationPolling(FOLLOWER_CORE_NAME); + request.setEnabled(null); + RemoteSolrException ex = + expectThrows( + RemoteSolrException.class, + () -> request.process(solrTestRule.getSolrClient(FOLLOWER_CORE_NAME))); + assertEquals(SolrException.ErrorCode.BAD_REQUEST.code, ex.code()); + } + +} From 9505e24c2c79009cd29d504c6c8f154e5994e858 Mon Sep 17 00:00:00 2001 From: jaykay12 Date: Sat, 31 Oct 2026 23:22:53 +0530 Subject: [PATCH 11/15] tidying --- .../solr/handler/admin/api/SetReplicationPollingAPITest.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/solr/core/src/test/org/apache/solr/handler/admin/api/SetReplicationPollingAPITest.java b/solr/core/src/test/org/apache/solr/handler/admin/api/SetReplicationPollingAPITest.java index 38d1f29ed90..b2580c82f07 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/api/SetReplicationPollingAPITest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/api/SetReplicationPollingAPITest.java @@ -20,8 +20,7 @@ public class SetReplicationPollingAPITest extends SolrTestCase { private static final String CORE_NAME = "setReplicationPollingApiTestCore"; private static final String FOLLOWER_CORE_NAME = "setReplicationPollingApiTestFollowerCore"; - @ClassRule - public static final SolrJettyTestRule solrTestRule = new SolrJettyTestRule(); + @ClassRule public static final SolrJettyTestRule solrTestRule = new SolrJettyTestRule(); @BeforeClass public static void setupSolr() throws Exception { @@ -105,5 +104,4 @@ public void testNullEnabledIsRejected() { () -> request.process(solrTestRule.getSolrClient(FOLLOWER_CORE_NAME))); assertEquals(SolrException.ErrorCode.BAD_REQUEST.code, ex.code()); } - } From 683dfc74fd8e191038e88b27e8a59e287d94dfed Mon Sep 17 00:00:00 2001 From: jaykay12 Date: Sat, 31 Oct 2026 23:31:19 +0530 Subject: [PATCH 12/15] license --- .../admin/api/SetReplicationPollingAPITest.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/solr/core/src/test/org/apache/solr/handler/admin/api/SetReplicationPollingAPITest.java b/solr/core/src/test/org/apache/solr/handler/admin/api/SetReplicationPollingAPITest.java index b2580c82f07..82ef54012f5 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/api/SetReplicationPollingAPITest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/api/SetReplicationPollingAPITest.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.handler.admin.api; import static org.apache.solr.core.CoreContainer.ALLOW_PATHS_SYSPROP; From 2958d1e7f5754dfc41677a2594d5750e3ef1da45 Mon Sep 17 00:00:00 2001 From: jaykay12 Date: Sat, 31 Oct 2026 23:39:55 +0530 Subject: [PATCH 13/15] minor tweaks --- .../api/SetReplicationPollingAPITest.java | 36 +++++++++++-------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/solr/core/src/test/org/apache/solr/handler/admin/api/SetReplicationPollingAPITest.java b/solr/core/src/test/org/apache/solr/handler/admin/api/SetReplicationPollingAPITest.java index 82ef54012f5..56252026e27 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/api/SetReplicationPollingAPITest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/api/SetReplicationPollingAPITest.java @@ -27,6 +27,7 @@ import org.apache.solr.common.SolrException; import org.apache.solr.util.ExternalPaths; import org.apache.solr.util.SolrJettyTestRule; +import org.junit.Before; import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Test; @@ -61,6 +62,11 @@ public static void setupSolr() throws Exception { solrTestRule.newCollection(FOLLOWER_CORE_NAME).withConfigSet(followerConfigSet).create(); } + @Before + public void beforeEach() throws Exception { + setPolling(true); + } + @Test public void testDisableThenEnablePolling() throws Exception { assertEquals(Boolean.FALSE, fetchPollingDisabled()); @@ -84,21 +90,6 @@ public void testMissingEnabledIsRejectedAndStateUnchanged() throws Exception { assertEquals(Boolean.FALSE, fetchPollingDisabled()); } - private static void setPolling(boolean enabled) throws Exception { - var request = new ReplicationApi.SetReplicationPolling(FOLLOWER_CORE_NAME); - request.setEnabled(enabled); - var response = request.process(solrTestRule.getSolrClient(FOLLOWER_CORE_NAME)); - assertNull(response.error); - } - - private static Boolean fetchPollingDisabled() throws Exception { - var request = new ReplicationApi.FetchReplicationDetails(FOLLOWER_CORE_NAME); - return request.process(solrTestRule.getSolrClient(FOLLOWER_CORE_NAME)) - .details - .follower - .isPollingDisabled; - } - @Test public void testNoFollowerIsRejected() { var request = new ReplicationApi.SetReplicationPolling(CORE_NAME); @@ -120,4 +111,19 @@ public void testNullEnabledIsRejected() { () -> request.process(solrTestRule.getSolrClient(FOLLOWER_CORE_NAME))); assertEquals(SolrException.ErrorCode.BAD_REQUEST.code, ex.code()); } + + private static void setPolling(boolean enabled) throws Exception { + var request = new ReplicationApi.SetReplicationPolling(FOLLOWER_CORE_NAME); + request.setEnabled(enabled); + var response = request.process(solrTestRule.getSolrClient(FOLLOWER_CORE_NAME)); + assertNull(response.error); + } + + private static Boolean fetchPollingDisabled() throws Exception { + var request = new ReplicationApi.FetchReplicationDetails(FOLLOWER_CORE_NAME); + return request.process(solrTestRule.getSolrClient(FOLLOWER_CORE_NAME)) + .details + .follower + .isPollingDisabled; + } } From a5553f0c7bfbcba39742673fbbe5f88fab9c7032 Mon Sep 17 00:00:00 2001 From: jaykay12 Date: Sat, 31 Oct 2026 23:44:59 +0530 Subject: [PATCH 14/15] javadoc --- .../solr/handler/admin/api/SetReplicationPollingAPITest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/solr/core/src/test/org/apache/solr/handler/admin/api/SetReplicationPollingAPITest.java b/solr/core/src/test/org/apache/solr/handler/admin/api/SetReplicationPollingAPITest.java index 56252026e27..7adf3aacd77 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/api/SetReplicationPollingAPITest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/api/SetReplicationPollingAPITest.java @@ -32,6 +32,10 @@ import org.junit.ClassRule; import org.junit.Test; +/** + * HTTP tests for {@code PUT /api/cores/coreName/replication/polling} via the generated SolrJ + * request. + */ public class SetReplicationPollingAPITest extends SolrTestCase { private static final String CORE_NAME = "setReplicationPollingApiTestCore"; From 0da59ce41aa185a6ae23602e69bae8d1e6635875 Mon Sep 17 00:00:00 2001 From: jaykay12 Date: Sat, 31 Oct 2026 23:53:49 +0530 Subject: [PATCH 15/15] added logging back --- .../src/java/org/apache/solr/handler/ReplicationHandler.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java b/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java index 549d4daf736..726adac4d0c 100644 --- a/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java @@ -861,6 +861,8 @@ public FileInfo(long lasmodified, String name, long size, long checksum) { public boolean setPollingEnabled(boolean enabled) { if (pollingIndexFetcher == null) return false; pollDisabled.set(!enabled); + log.info( + "polling {}, value of pollDisabled = {}", enabled ? "enabled" : "disabled", pollDisabled); return true; }