Skip to content

Commit 4bf9603

Browse files
authored
Avoid self-suppression on grouped action listener (#53262)
It can be that a failure is repeated to a grouped action listener. For example, if the same exception such as a connect transport exception, is the cause of repeated failures. Previously we were unconditionally self-suppressing the exception into the first exception, but self-supressing is not allowed. Thus, we would throw an exception and the grouped action listener would never complete. This commit addresses this by guarding against self-suppression.
1 parent 04930e9 commit 4bf9603

File tree

2 files changed

+26
-3
lines changed

2 files changed

+26
-3
lines changed

server/src/main/java/org/elasticsearch/action/support/GroupedActionListener.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,12 @@ public void onResponse(T element) {
7171
@Override
7272
public void onFailure(Exception e) {
7373
if (failure.compareAndSet(null, e) == false) {
74-
failure.accumulateAndGet(e, (previous, current) -> {
75-
previous.addSuppressed(current);
76-
return previous;
74+
failure.accumulateAndGet(e, (current, update) -> {
75+
// we have to avoid self-suppression!
76+
if (update != current) {
77+
current.addSuppressed(update);
78+
}
79+
return current;
7780
});
7881
}
7982
if (countDown.countDown()) {

server/src/test/java/org/elasticsearch/action/support/GroupedActionListenerTests.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@
3333
import java.util.concurrent.atomic.AtomicReference;
3434

3535
import static org.hamcrest.CoreMatchers.instanceOf;
36+
import static org.hamcrest.CoreMatchers.nullValue;
37+
import static org.hamcrest.Matchers.equalTo;
38+
import static org.hamcrest.Matchers.not;
3639

3740
public class GroupedActionListenerTests extends ESTestCase {
3841

@@ -133,4 +136,21 @@ public void testConcurrentFailures() throws InterruptedException {
133136
assertThat(exception, instanceOf(IOException.class));
134137
assertEquals(numGroups - 1, exception.getSuppressed().length);
135138
}
139+
140+
/*
141+
* It can happen that the same exception causes a grouped listener to be notified of the failure multiple times. Since we suppress
142+
* additional exceptions into the first exception, we have to guard against suppressing into the same exception, which could occur if we
143+
* are notified of with the same failure multiple times. This test verifies that the guard against self-suppression remains.
144+
*/
145+
public void testRepeatNotificationForTheSameException() {
146+
final AtomicReference<Exception> finalException = new AtomicReference<>();
147+
final GroupedActionListener<Void> listener = new GroupedActionListener<>(ActionListener.wrap(r -> {}, finalException::set), 2);
148+
final Exception e = new Exception();
149+
// repeat notification for the same exception
150+
listener.onFailure(e);
151+
listener.onFailure(e);
152+
assertThat(finalException.get(), not(nullValue()));
153+
assertThat(finalException.get(), equalTo(e));
154+
}
155+
136156
}

0 commit comments

Comments
 (0)