Skip to content

Commit 8fd823e

Browse files
authored
SOLR-18086: Fix LBSolrClient behavior in case of zombie nodes (#4130)
1 parent 30fd00c commit 8fd823e

File tree

3 files changed

+54
-11
lines changed

3 files changed

+54
-11
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc
2+
title: Stale EndpointIterator state causes unexpected order when there are zombie servers in LBSolrClient
3+
type: fixed # added, changed, fixed, deprecated, removed, dependency_update, security, other
4+
authors:
5+
- name: Chris Hostetter
6+
- name: Anshum Gupta
7+
links:
8+
- name: SOLR-18086
9+
url: https://issues.apache.org/jira/browse/SOLR-18086

solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBSolrClient.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,7 @@ private void fetchNext() {
383383
skipped.add(wrapper.getEndpoint());
384384
}
385385
}
386+
endpoint = null;
386387
continue;
387388
}
388389

solr/solrj/src/test/org/apache/solr/client/solrj/impl/LBSolrClientTest.java

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -96,20 +96,53 @@ public void testServerIterator() throws SolrServerException {
9696
public void testServerIteratorWithZombieServers() throws SolrServerException {
9797
HashMap<String, LBSolrClient.EndpointWrapper> zombieServers = new HashMap<>();
9898
LBSolrClient.Req req = new LBSolrClient.Req(new QueryRequest(), SOLR_ENDPOINTS);
99-
LBSolrClient.EndpointIterator endpointIterator =
99+
100+
// pick a random number of endpoints to mark as zombies
101+
final int numToShuffle = random().nextInt(SOLR_ENDPOINTS.size());
102+
// yes, this might pick the same endpoint multiple times; fine for our purposes
103+
for (int i = 0; i < numToShuffle; i++) {
104+
final LBSolrClient.Endpoint z = SOLR_ENDPOINTS.get(random().nextInt(SOLR_ENDPOINTS.size()));
105+
zombieServers.put(z.getUrl(), new LBSolrClient.EndpointWrapper(z));
106+
}
107+
// Try those on the Zombie list after all other possibilities are exhausted.
108+
final List<LBSolrClient.Endpoint> expectedOrder = new ArrayList<>(SOLR_ENDPOINTS);
109+
for (LBSolrClient.Endpoint e : SOLR_ENDPOINTS) {
110+
if (zombieServers.containsKey(e.getUrl())) {
111+
expectedOrder.remove(e);
112+
expectedOrder.add(e);
113+
}
114+
}
115+
116+
final LBSolrClient.EndpointIterator endpointIterator =
100117
new LBSolrClient.EndpointIterator(req, zombieServers);
101-
zombieServers.put("2", new LBSolrClient.EndpointWrapper(new LBSolrClient.Endpoint("2")));
118+
final List<LBSolrClient.Endpoint> actualOrder = new ArrayList<>();
119+
while (endpointIterator.hasNext()) {
120+
actualOrder.add(endpointIterator.nextOrError());
121+
}
122+
assertFalse(endpointIterator.hasNext()); // sanity check double call
123+
assertEquals("randomZombies(" + zombieServers.keySet() + ")", expectedOrder, actualOrder);
124+
LuceneTestCase.expectThrows(SolrServerException.class, endpointIterator::nextOrError);
125+
}
102126

103-
assertTrue(endpointIterator.hasNext());
104-
assertEquals(new LBSolrClient.Endpoint("1"), endpointIterator.nextOrError());
105-
assertTrue(endpointIterator.hasNext());
106-
assertEquals(new LBSolrClient.Endpoint("3"), endpointIterator.nextOrError());
107-
assertTrue(endpointIterator.hasNext());
108-
assertEquals(new LBSolrClient.Endpoint("4"), endpointIterator.nextOrError());
127+
@Test
128+
public void testServerIteratorWithAllZombies() throws SolrServerException {
129+
HashMap<String, LBSolrClient.EndpointWrapper> zombieServers = new HashMap<>();
130+
LBSolrClient.Req req = new LBSolrClient.Req(new QueryRequest(), SOLR_ENDPOINTS);
131+
for (LBSolrClient.Endpoint e : SOLR_ENDPOINTS) {
132+
zombieServers.put(e.getBaseUrl(), new LBSolrClient.EndpointWrapper(e));
133+
}
109134

110-
// Try those on the Zombie list after all other possibilities are exhausted.
111-
assertTrue(endpointIterator.hasNext());
112-
assertEquals(new LBSolrClient.Endpoint("2"), endpointIterator.nextOrError());
135+
final LBSolrClient.EndpointIterator endpointIterator =
136+
new LBSolrClient.EndpointIterator(req, zombieServers);
137+
// if everyone is a zombie, then the original sorted preference order of
138+
// endpoints should still be respected
139+
final List<LBSolrClient.Endpoint> actualOrder = new ArrayList<>();
140+
while (endpointIterator.hasNext()) {
141+
actualOrder.add(endpointIterator.nextOrError());
142+
}
143+
assertFalse(endpointIterator.hasNext()); // sanity check double call
144+
assertEquals(SOLR_ENDPOINTS, actualOrder);
145+
LuceneTestCase.expectThrows(SolrServerException.class, endpointIterator::nextOrError);
113146
}
114147

115148
@Test

0 commit comments

Comments
 (0)