https://github.com/elastic/elasticsearch/blob/099fb84cfcf87be6689c3516674f45438bafa767/server/src/main/java/org/elasticsearch/action/ActionListener.java#L420-L432

Rather than keeping hold of the `delegate` and using an `AtomicBoolean` to track the completion, we could use an `AtomicReference` and drop the whole listener on completion, which might free up heap in some fail-fast cases.
server/src/test/java/org/elasticsearch/action/NotifyOnceListenerTests.java
}
        };
        listener.onResponse("response");
        listener.onResponse("wrong-response");
        listener.onFailure(new RuntimeException());
        assertNull(exception.get());
        assertEquals("response", response.get());
    }
    public void testWhenErrorCannotNotifyMultipleTimes() {
        AtomicReference<String> response = new AtomicReference<>();
        AtomicReference<Exception> exception = new AtomicReference<>();
        NotifyOnceListener<String> listener = new NotifyOnceListener<String>() {
            @Override
            public void innerOnResponse(String s) {
                response.set(s);
            }
            @Override
            public void innerOnFailure(Exception e) {
                exception.set(e);
            }
        };
        RuntimeException expected = new RuntimeException();
        listener.onFailure(expected);
        listener.onFailure(new IllegalArgumentException());
        listener.onResponse("response");
        assertNull(response.get());
        assertSame(expected, exception.get());
    }
}