Java high-level REST client: There is no way to recover after a `ContentTooLongException` and `IOReactorException`

Hi,

version: 7.9.1

Context
We are using the Java high-level REST client.
When executing a search request, and the response's size exceeds the buffered heap, then a ContentTooLongException and IOReactorException are thrown. By default, the heap is initialized to 100Mb.
For all subsequent requests, the REST client will throw a RuntimeException, even if the heap is larger than the size of the response entity. There seems to be no way to fix the situation without restarting the application. I'd expect that subsequent requests would not fail assuming they were small enough to fit into the max heap buffered response.

Stacktrace

  • ContentTooLongException
org.apache.http.ContentTooLongException: entity content is too long [147830] for the configured buffer limit [1024]
	at org.elasticsearch.client.HeapBufferedAsyncResponseConsumer.onEntityEnclosed(HeapBufferedAsyncResponseConsumer.java:76)
	at org.apache.http.nio.protocol.AbstractAsyncResponseConsumer.responseReceived(AbstractAsyncResponseConsumer.java:137)
	at org.apache.http.impl.nio.client.MainClientExec.responseReceived(MainClientExec.java:315)
	at org.apache.http.impl.nio.client.DefaultClientExchangeHandlerImpl.responseReceived(DefaultClientExchangeHandlerImpl.java:151)
	at org.apache.http.nio.protocol.HttpAsyncRequestExecutor.responseReceived(HttpAsyncRequestExecutor.java:315)
	at org.apache.http.impl.nio.DefaultNHttpClientConnection.consumeInput(DefaultNHttpClientConnection.java:255)
	at org.apache.http.impl.nio.client.InternalIODispatch.onInputReady(InternalIODispatch.java:81)
	at org.apache.http.impl.nio.client.InternalIODispatch.onInputReady(InternalIODispatch.java:39)
	at org.apache.http.impl.nio.reactor.AbstractIODispatch.inputReady(AbstractIODispatch.java:114)
	at org.apache.http.impl.nio.reactor.BaseIOReactor.readable(BaseIOReactor.java:162)
	at org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvent(AbstractIOReactor.java:337)
	at org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvents(AbstractIOReactor.java:315)
	at org.apache.http.impl.nio.reactor.AbstractIOReactor.execute(AbstractIOReactor.java:276)
	at org.apache.http.impl.nio.reactor.BaseIOReactor.execute(BaseIOReactor.java:104)
	at org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor$Worker.run(AbstractMultiworkerIOReactor.java:591)
	at java.base/java.lang.Thread.run(Thread.java:834)
  • IOReactorException
org.apache.http.nio.reactor.IOReactorException: I/O dispatch worker terminated abnormally
	at org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor.execute(AbstractMultiworkerIOReactor.java:359)
	at org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager.execute(PoolingNHttpClientConnectionManager.java:221)
	at org.apache.http.impl.nio.client.CloseableHttpAsyncClientBase$1.run(CloseableHttpAsyncClientBase.java:64)
	at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.IllegalArgumentException: Incorrect number of labels.
	at io.prometheus.client.SimpleCollector.labels(SimpleCollector.java:64)
	at com.blablacar.trip.common.config.CustomHttpAsyncRequestExecutor.exception(CustomHttpAsyncRequestExecutor.java:37)
	at org.apache.http.impl.nio.DefaultNHttpClientConnection.consumeInput(DefaultNHttpClientConnection.java:276)
	at org.apache.http.impl.nio.client.InternalIODispatch.onInputReady(InternalIODispatch.java:81)
	at org.apache.http.impl.nio.client.InternalIODispatch.onInputReady(InternalIODispatch.java:39)
	at org.apache.http.impl.nio.reactor.AbstractIODispatch.inputReady(AbstractIODispatch.java:114)
	at org.apache.http.impl.nio.reactor.BaseIOReactor.readable(BaseIOReactor.java:162)
	at org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvent(AbstractIOReactor.java:337)
	at org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvents(AbstractIOReactor.java:315)
	at org.apache.http.impl.nio.reactor.AbstractIOReactor.execute(AbstractIOReactor.java:276)
	at org.apache.http.impl.nio.reactor.BaseIOReactor.execute(BaseIOReactor.java:104)
	at org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor$Worker.run(AbstractMultiworkerIOReactor.java:591)
	... 1 common frames omitted
  • RuntimeException
java.lang.RuntimeException: Request cannot be executed; I/O reactor status: STOPPED
	at org.elasticsearch.client.RestClient.extractAndWrapCause(RestClient.java:857)
	at org.elasticsearch.client.RestClient.performRequest(RestClient.java:259)
	at org.elasticsearch.client.RestClient.performRequest(RestClient.java:246)
	at org.elasticsearch.client.RestHighLevelClient.internalPerformRequest(RestHighLevelClient.java:1613)
	at org.elasticsearch.client.RestHighLevelClient.performRequest(RestHighLevelClient.java:1583)
	at org.elasticsearch.client.RestHighLevelClient.performRequestAndParseEntity(RestHighLevelClient.java:1553)
	at org.elasticsearch.client.RestHighLevelClient.search(RestHighLevelClient.java:1069)
	
	…. 

	at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1700)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.IllegalStateException: Request cannot be executed; I/O reactor status: STOPPED
	at org.apache.http.util.Asserts.check(Asserts.java:46)
	at org.apache.http.impl.nio.client.CloseableHttpAsyncClientBase.ensureRunning(CloseableHttpAsyncClientBase.java:90)
	at org.apache.http.impl.nio.client.InternalHttpAsyncClient.execute(InternalHttpAsyncClient.java:123)
	at org.elasticsearch.client.RestClient.performRequest(RestClient.java:255)
	... 28 common frames omitted

How to reproduce
We can reproduce the problem by playing with the heap size. In the snippet of code below, the very first search will use a 1Kb heap. All others will use a 100Mb heap size.
The first search will throw both the ContentTooLongException and IOReactorException.
Allo others will throw the RuntimeException even if the heap is larger than the size of the response entity.

Snippet of code:

class DataSource {

    // Very small heap size
    private static final int MAX_HEAP_BUFFER_LIMIT_IN_BYTES_1K = 1024;
    // Default heap size 100Mb
    private static final int MAX_HEAP_BUFFER_LIMIT_IN_BYTES_100M = 100 * 1024 * 1024;

    private final RestHighLevelClient client;
    /**
     * Switch the value of the buffered heap.
     * For the very first call, the heap equals MAX_HEAP_BUFFER_LIMIT_IN_BYTES_1K,
     * and MAX_HEAP_BUFFER_LIMIT_IN_BYTES_100M for all subsequent calls.
     */
    private final AtomicBoolean heapSwitch;

    public DataSource(RestHighLevelClient client) {
        this.client = client;
        this.heapSwitch = new AtomicBoolean(false);
    }

    public List<String> search() {
        SearchRequest searchRequest = new SearchRequest("index-1")
            .requestCache(true)
            .source(new SearchSourceBuilder().query(boolQuery()));

        // Choose the heap value.
        int heap;
        if (!heapSwitch.get()) {
            heap = MAX_HEAP_BUFFER_LIMIT_IN_BYTES_1K;
            heapSwitch.getAndSet(true);
        } else {
            heap = MAX_HEAP_BUFFER_LIMIT_IN_BYTES_100M;
        }

        Builder customOptions = RequestOptions.DEFAULT.toBuilder();
        customOptions.setHttpAsyncResponseConsumerFactory(
            new HeapBufferedResponseConsumerFactory(heap)
        );

        try {
            SearchResponse response = client.search(searchRequest, customOptions.build());

            return ...;
        } catch (IOException e) {
            return ...;
        }
    }
}

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.