Skip to content

Commit a15e6f4

Browse files
committed
SOLR-17413: ulog replay should copy SolrQueryRequest (#2765)
SolrQueryRequest is a non-threadsafe type, but was being shared across executor threads during UpdateLog replay. This introduces a number of issues, not the least being a ConcurrentModificationException if multiple threads happen to tweak the request 'context' simultaneously. This commit fixes this issue by giving each executor thread a unique LocalSolrQueryRequest instance to use.
1 parent f4150f7 commit a15e6f4

File tree

2 files changed

+16
-5
lines changed

2 files changed

+16
-5
lines changed

solr/CHANGES.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ Bug Fixes
8181

8282
* SOLR-17464: Fixed Http2SolrClient bug in that 'requestAsync' triggered NPE when using a shared Jetty client (Jason Gerlowski, James Dyer)
8383

84+
* SOLR-17413: Fixed UpdateLog replay bug that shared thread-unsafe SolrQueryRequest objects across threads (Jason Gerlowski, David Smiley, Houston Putman)
85+
8486
Dependency Upgrades
8587
---------------------
8688
(No changes)

solr/core/src/java/org/apache/solr/update/UpdateLog.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
import org.apache.solr.common.SolrException;
6464
import org.apache.solr.common.SolrException.ErrorCode;
6565
import org.apache.solr.common.SolrInputDocument;
66+
import org.apache.solr.common.params.MapSolrParams;
6667
import org.apache.solr.common.params.ModifiableSolrParams;
6768
import org.apache.solr.common.params.SolrParams;
6869
import org.apache.solr.common.util.CollectionUtil;
@@ -1998,6 +1999,14 @@ public String toString() {
19981999
protected RecoveryInfo recoveryInfo;
19992000

20002001
class LogReplayer implements Runnable {
2002+
private final SolrParams BASE_REPLAY_PARAMS =
2003+
new MapSolrParams(
2004+
Map.of(
2005+
DISTRIB_UPDATE_PARAM,
2006+
FROMLEADER.toString(),
2007+
DistributedUpdateProcessor.LOG_REPLAY,
2008+
"true"));
2009+
20012010
private Logger loglog = log; // set to something different?
20022011

20032012
Deque<TransactionLog> translogs;
@@ -2024,10 +2033,7 @@ public LogReplayer(List<TransactionLog> translogs, boolean activeLog, boolean in
20242033

20252034
@Override
20262035
public void run() {
2027-
ModifiableSolrParams params = new ModifiableSolrParams();
2028-
params.set(DISTRIB_UPDATE_PARAM, FROMLEADER.toString());
2029-
params.set(DistributedUpdateProcessor.LOG_REPLAY, "true");
2030-
req = new LocalSolrQueryRequest(uhandler.core, params);
2036+
req = new LocalSolrQueryRequest(uhandler.core, BASE_REPLAY_PARAMS);
20312037
rsp = new SolrQueryResponse();
20322038
// setting request info will help logging
20332039
SolrRequestInfo.setRequestInfo(new SolrRequestInfo(req, rsp));
@@ -2100,7 +2106,10 @@ public void doReplay(TransactionLog translog) {
21002106
ThreadLocal<UpdateRequestProcessor> procThreadLocal =
21012107
ThreadLocal.withInitial(
21022108
() -> {
2103-
var proc = processorChain.createProcessor(req, rsp);
2109+
// SolrQueryRequest is not thread-safe, so use a copy when creating URPs
2110+
final var localRequest =
2111+
new LocalSolrQueryRequest(uhandler.core, BASE_REPLAY_PARAMS);
2112+
var proc = processorChain.createProcessor(localRequest, rsp);
21042113
procPool.add(proc);
21052114
return proc;
21062115
});

0 commit comments

Comments
 (0)