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/main/java/org/elasticsearch/action/ActionListener.java
private final BiConsumer<ActionListener<T>, Exception> bc;
        DelegatingActionListener(ActionListener<T> delegate, BiConsumer<ActionListener<T>, Exception> bc) {
            super(delegate);
            this.bc = bc;
        }
        @Override
        public void onResponse(T t) {
            delegate.onResponse(t);
        }
        @Override
        public void onFailure(Exception e) {
            try {
                bc.accept(delegate, e);
            } catch (RuntimeException ex) {
                if (ex != e) {
                    ex.addSuppressed(e);
                }
                assert false : new AssertionError("listener.onFailure failed", ex);
                throw ex;
            }
        }
        @Override
        public String toString() {
            return super.toString() + "/" + bc;
        }
    }
    final class DelegatingFailureActionListener<T, R> extends Delegating<T, R> {