Skip to content

Commit 4aec5ef

Browse files
committed
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 1fa12bd commit 4aec5ef

File tree

2 files changed

+27
-3
lines changed

2 files changed

+27
-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
@@ -72,9 +72,12 @@ public void onResponse(T element) {
7272
@Override
7373
public void onFailure(Exception e) {
7474
if (failure.compareAndSet(null, e) == false) {
75-
failure.accumulateAndGet(e, (previous, current) -> {
76-
previous.addSuppressed(current);
77-
return previous;
75+
failure.accumulateAndGet(e, (current, update) -> {
76+
// we have to avoid self-suppression!
77+
if (update != current) {
78+
current.addSuppressed(update);
79+
}
80+
return current;
7881
});
7982
}
8083
if (countDown.countDown()) {

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

Lines changed: 21 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

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

0 commit comments

Comments
 (0)