Skip to content
Merged
9 changes: 9 additions & 0 deletions changelog/unreleased/SOLR-16396-config-get-others.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
title:
Migrate GET /config/overlay and GET /config/znodeVersion to JAX-RS.
type: changed
authors:
- name: Jalaz Kumar
nick: jaykay12
links:
- name: SOLR-16396
url: https://issues.apache.org/jira/browse/SOLR-16396
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import jakarta.ws.rs.Path;
import jakarta.ws.rs.QueryParam;
import org.apache.solr.client.api.model.ConfigInfoResponse;
import org.apache.solr.client.api.model.ConfigOverlayResponse;
import org.apache.solr.client.api.model.ConfigZnodeVersionResponse;
import org.apache.solr.client.api.util.StoreApiParameters;

/** V2 API for reading and modifying a core/collection's config */
Expand All @@ -42,5 +44,29 @@ ConfigInfoResponse getConfig(
@DefaultValue("false")
@Parameter(description = "Expand request handler useParams and effective parameters")
boolean expandParams);

@GET
@Path("/overlay")
@StoreApiParameters
@Operation(
summary = "Fetch the config overlay of the specified core or collection",
tags = {"config"})
ConfigOverlayResponse getOverlay();

@GET
@Path("/znodeVersion")
@StoreApiParameters
@Operation(
summary = "Fetch znode versions for config overlay and request params",
tags = {"config"})
ConfigZnodeVersionResponse getZnodeVersion(
@QueryParam("overlay")
@DefaultValue("-1")
@Parameter(description = "Expected overlay znode version")
int expectedOverlayVersion,
@QueryParam("params")
@DefaultValue("-1")
@Parameter(description = "Expected request-params znode version")
int expectedParamsVersion);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* 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;
import java.util.Map;

public class ConfigOverlayResponse extends SolrJerseyResponse {
@JsonProperty("overlay")
public Map<String, Object> overlay;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* 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;

public class ConfigZnodeVersionResponse extends SolrJerseyResponse {
@JsonProperty("znodeVersion")
public ZnodeVersions znodeVersion;

public static class ZnodeVersions {
@JsonProperty("overlay")
public int overlay;

@JsonProperty("params")
public int params;
}
}
122 changes: 60 additions & 62 deletions solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ public static boolean getImmutable(SolrCore core) {
return immutable != null && Boolean.parseBoolean(immutable.toString());
}

private class Command {
private static class Command {
private final SolrQueryRequest req;
private final SolrQueryResponse resp;
private final String method;
Expand Down Expand Up @@ -202,7 +202,7 @@ private void handleGET() {
resp, new GetConfig(req).getConfig(req.getParams().getBool("expandParams", false)));
} else {
if (ConfigOverlay.NAME.equals(parts.get(1))) {
resp.add(ConfigOverlay.NAME, req.getCore().getSolrConfig().getOverlay());
V2ApiUtils.squashIntoSolrResponseWithoutHeader(resp, new GetConfig(req).getOverlay());
} else if (RequestParams.NAME.equals(parts.get(1))) {
if (parts.size() == 3) {
RequestParams params = req.getCore().getSolrConfig().getRequestParams();
Expand All @@ -219,66 +219,12 @@ private void handleGET() {

} else {
if (ZNODEVER.equals(parts.get(1))) {
resp.add(
ZNODEVER,
Map.of(
ConfigOverlay.NAME,
req.getCore().getSolrConfig().getOverlay().getVersion(),
RequestParams.NAME,
req.getCore().getSolrConfig().getRequestParams().getZnodeVersion()));
boolean isStale = false;
int expectedVersion = req.getParams().getInt(ConfigOverlay.NAME, -1);
int actualVersion = req.getCore().getSolrConfig().getOverlay().getVersion();
if (expectedVersion > actualVersion) {
log.info(
"expecting overlay version {} but my version is {}",
expectedVersion,
actualVersion);
isStale = true;
} else if (expectedVersion != -1) {
log.info("I already have the expected version {} of config", expectedVersion);
}
expectedVersion = req.getParams().getInt(RequestParams.NAME, -1);
actualVersion = req.getCore().getSolrConfig().getRequestParams().getZnodeVersion();
if (expectedVersion > actualVersion) {
log.info(
"expecting params version {} but my version is {}",
expectedVersion,
actualVersion);
isStale = true;
} else if (expectedVersion != -1) {
log.info("I already have the expected version {} of params", expectedVersion);
}
if (isStale && req.getCore().getResourceLoader() instanceof ZkSolrResourceLoader) {
new Thread(
() -> {
if (!reloadLock.tryLock()) {
log.info("Another reload is in progress . Not doing anything");
return;
}
try {
log.info("Trying to update my configs");
SolrCore.getConfListener(
req.getCore(),
(ZkSolrResourceLoader) req.getCore().getResourceLoader())
.run();
} catch (Exception e) {
log.error("Unable to refresh conf ", e);
} finally {
reloadLock.unlock();
}
},
SolrConfigHandler.class.getSimpleName() + "-refreshconf")
.start();
} else {
if (log.isInfoEnabled()) {
log.info(
"isStale {} , resourceloader {}",
isStale,
req.getCore().getResourceLoader().getClass().getName());
}
}

V2ApiUtils.squashIntoSolrResponseWithoutHeader(
resp,
new GetConfig(req)
.getZnodeVersion(
req.getParams().getInt(ConfigOverlay.NAME, -1),
req.getParams().getInt(RequestParams.NAME, -1)));
} else {
Map<String, Object> m = getConfigDetails(parts.get(1), req);
Map<String, Object> val = new LinkedHashMap<>();
Expand Down Expand Up @@ -970,6 +916,58 @@ public static List<Replica> getActiveReplicas(ZkController zkController, String
return activeReplicas;
}

public static void maybeRefreshStaleConfig(
SolrCore core, int expectedOverlayVersion, int expectedParamsVersion) {
boolean isStale =
isExpectedVersionStale(
"overlay", expectedOverlayVersion, core.getSolrConfig().getOverlay().getVersion())
| isExpectedVersionStale(
"params",
expectedParamsVersion,
core.getSolrConfig().getRequestParams().getZnodeVersion());
if (isStale && core.getResourceLoader() instanceof ZkSolrResourceLoader) {
final Lock reloadLock =
((SolrConfigHandler) core.getRequestHandler("/config")).getReloadLock();
new Thread(
() -> {
if (!reloadLock.tryLock()) {
log.info("Another reload is in progress . Not doing anything");
return;
}
try {
log.info("Trying to update my configs");
SolrCore.getConfListener(core, (ZkSolrResourceLoader) core.getResourceLoader())
.run();
} catch (Exception e) {
log.error("Unable to refresh conf ", e);
} finally {
reloadLock.unlock();
}
},
SolrConfigHandler.class.getSimpleName() + "-refreshconf")
.start();
} else {
if (log.isInfoEnabled()) {
log.info(
"isStale {} , resourceloader {}",
isStale,
core.getResourceLoader().getClass().getName());
}
}
}

private static boolean isExpectedVersionStale(String label, int expected, int actual) {
if (expected == -1) {
return false;
}
if (expected > actual) {
log.info("expecting {} version {} but my version is {}", label, expected, actual);
return true;
}
log.info("I already have the expected version {} of {}", expected, label);
return false;
}

@Override
public Name getPermissionName(AuthorizationContext ctx) {
switch (ctx.getHttpMethod()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,16 @@
import org.apache.solr.api.JerseyResource;
import org.apache.solr.client.api.endpoint.ConfigApi;
import org.apache.solr.client.api.model.ConfigInfoResponse;
import org.apache.solr.client.api.model.ConfigOverlayResponse;
import org.apache.solr.client.api.model.ConfigZnodeVersionResponse;
import org.apache.solr.common.params.MapSolrParams;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.StrUtils;
import org.apache.solr.common.util.Utils;
import org.apache.solr.core.PluginInfo;
import org.apache.solr.handler.RequestHandlerBase;
import org.apache.solr.handler.SolrConfigHandler;
import org.apache.solr.jersey.NullKeyTolerantMap;
import org.apache.solr.jersey.PermissionName;
import org.apache.solr.request.SolrQueryRequest;
Expand All @@ -60,6 +63,32 @@ public ConfigInfoResponse getConfig(boolean expandParams) {
return response;
}

@Override
@PermissionName(CONFIG_READ_PERM)
public ConfigOverlayResponse getOverlay() {
final var response = instantiateJerseyResponse(ConfigOverlayResponse.class);
response.overlay =
Utils.convertToMap(
solrQueryRequest.getCore().getSolrConfig().getOverlay(), new LinkedHashMap<>());
return response;
}

@Override
@PermissionName(CONFIG_READ_PERM)
public ConfigZnodeVersionResponse getZnodeVersion(
int expectedOverlayVersion, int expectedParamsVersion) {
final var response = instantiateJerseyResponse(ConfigZnodeVersionResponse.class);
final var core = solrQueryRequest.getCore();
final var solrConfig = core.getSolrConfig();

response.znodeVersion = new ConfigZnodeVersionResponse.ZnodeVersions();
response.znodeVersion.overlay = solrConfig.getOverlay().getVersion();
response.znodeVersion.params = solrConfig.getRequestParams().getZnodeVersion();

SolrConfigHandler.maybeRefreshStaleConfig(core, expectedOverlayVersion, expectedParamsVersion);
return response;
}

@SuppressWarnings({"unchecked"})
private Map<String, Object> buildConfigMap(
SolrQueryRequest solrQueryRequest, boolean expandParams) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,6 @@ public void getSingleParamSet(SolrQueryRequest req, SolrQueryResponse rsp) {
// Delete a path from this list when that section is migrated to Jersey
@EndPoint(
path = {
"/config/overlay",
"/config/znodeVersion",
"/config/luceneMatchVersion",
"/config/query",
"/config/requestHandler",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,9 @@ public void populateApiBag() {
// the API lookup works for a handful of the valid config "components".
@Test
public void testGetSingleComponentConfig() {
assertAnnotatedApiExistsFor("GET", "/config/overlay");
assertAnnotatedApiExistsFor("GET", "/config/query");
assertAnnotatedApiExistsFor("GET", "/config/jmx");
assertAnnotatedApiExistsFor("GET", "/config/requestDispatcher");
assertAnnotatedApiExistsFor("GET", "/config/znodeVersion");
assertAnnotatedApiExistsFor("GET", "/config/luceneMatchVersion");
assertAnnotatedApiExistsFor("GET", "/config/indexConfigdeletionPolicy");
assertAnnotatedApiExistsFor("GET", "/config/updateHandlerupdateLog");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public static void setupCluster() throws Exception {
}

@Test
public void testGetConfigFromCore() throws Exception {
public void testGetConfigFromCollection() throws Exception {
var request = new ConfigApi.GetConfig(IndexType.COLLECTION, COLLECTION_NAME);
var response = request.process(cluster.getSolrClient());

Expand All @@ -52,4 +52,28 @@ public void testGetConfigFromCore() throws Exception {
assertTrue(response.config.containsKey("query"));
assertTrue(response.config.containsKey("requestHandler"));
}

@Test
public void testGetOverlayFromCollection() throws Exception {
var request = new ConfigApi.GetOverlay(IndexType.COLLECTION, COLLECTION_NAME);
var response = request.process(cluster.getSolrClient());

assertNotNull(response);
assertNull(response.error);
assertNotNull(response.overlay);
assertTrue(response.overlay.containsKey("znodeVersion"));
assertEquals(-1, response.overlay.get("znodeVersion"));
}

@Test
public void testGetZnodeVersionFromCollection() throws Exception {
var request = new ConfigApi.GetZnodeVersion(IndexType.COLLECTION, COLLECTION_NAME);
var response = request.process(cluster.getSolrClient());

assertNotNull(response);
assertNull(response.error);
assertNotNull(response.znodeVersion);
assertTrue(response.znodeVersion.overlay >= -1);
assertTrue(response.znodeVersion.params >= -1);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,28 @@ public void testGetConfigFromCore() throws Exception {
assertTrue(response.config.containsKey("searchComponent"));
assertTrue(response.config.containsKey("updateProcessor"));
}

@Test
public void testGetOverlayFromCore() throws Exception {
var request = new ConfigApi.GetOverlay(IndexType.CORE, CORE_NAME);
var response = request.process(solrTestRule.getSolrClient(CORE_NAME));

assertNotNull(response);
assertNull(response.error);
assertNotNull(response.overlay);
assertTrue(response.overlay.containsKey("znodeVersion"));
assertEquals(-1, response.overlay.get("znodeVersion"));
}

@Test
public void testGetZnodeVersionFromCore() throws Exception {
var request = new ConfigApi.GetZnodeVersion(IndexType.CORE, CORE_NAME);
var response = request.process(solrTestRule.getSolrClient(CORE_NAME));

assertNotNull(response);
assertNull(response.error);
assertNotNull(response.znodeVersion);
assertTrue(response.znodeVersion.overlay >= -1);
assertTrue(response.znodeVersion.params >= -1);
}
}
Loading
Loading