vert.x icon indicating copy to clipboard operation
vert.x copied to clipboard

Vertx starts streaming buffer before onSuccess is completed

Open kvr000 opened this issue 1 month ago • 1 comments

Version

5.0.5

Context

OS: Linux 6.17.0-6-generic #6-Ubuntu SMP PREEMPT_DYNAMIC Tue Oct 7 13:34:17 UTC 2025 x86_64 GNU/Linux (Kubuntu 25.10) JVM: openjdk 21.0.9 2025-10-21

project link: https://github.com/kvr000/zbynek-java-exp/tree/master/http-exp/http-benchmark

In HttpClient streaming, Vertx seems to start pushing buffers prior to Future.onSuccess((HttpResponse) -> {}) is completed. As the stream handler is typically in that function, this results in loss of data:

In the github link, I provided benchmark, b3_VertxHttpClient is the randomly failing method - typically it misses about 2000 bytes - that indicates very close race condition.

In order to simulate bigger difference, I added sleep(1000) before registering onSuccess() callback and then it misses 1 GB.

Here is the additional snippet to the project:

	@Benchmark
	public void a0_VertxHttpClient(VertxHttpClientState state, Blackhole blackhole) throws Exception
	{
		MutableLong size = new MutableLong();
		Future<HttpClientResponse> future = state.httpClient.request(HttpMethod.GET, "/big_16GB")
			.compose(HttpClientRequest::send);
		Thread.sleep(1000);
		future.onSuccess((HttpClientResponse response) -> {
			if (response.statusCode() != 200) {
				Try.of(() -> { throw new IOException("Failed to retrieve file: status=" + response.statusCode()); }).get();
			}
			response.handler((buffer) -> size.add(buffer.length()));
		});
		future.await().end().await();
		if (future.await().statusCode() != 200) {
			throw new IllegalStateException("Unexpected status: " + future.await().statusCode());
		}
		if (size.longValue() != 16L*1024*1024*1024) {
			throw new IllegalStateException("Unexpected size: " + size);
		}
		blackhole.consume(size.longValue());
	}

Steps to reproduce

  1. Run the benchmark provided in the project.
  2. b3_VertxHttpClient will randomly fail with exception: java.io.IOException: Unexpected size: 17179865088

More reliable failure:

	@Benchmark
	public void a0_VertxHttpClient(VertxHttpClientState state, Blackhole blackhole) throws Exception
	{
		MutableLong size = new MutableLong();
		Future<HttpClientResponse> future = state.httpClient.request(HttpMethod.GET, "/big_16GB")
			.compose(HttpClientRequest::send);
		Thread.sleep(1000);
		future.onSuccess((HttpClientResponse response) -> {
			if (response.statusCode() != 200) {
				Try.of(() -> { throw new IOException("Failed to retrieve file: status=" + response.statusCode()); }).get();
			}
			response.handler((buffer) -> size.add(buffer.length()));
		});
		future.await().end().await();
		if (future.await().statusCode() != 200) {
			throw new IllegalStateException("Unexpected status: " + future.await().statusCode());
		}
		if (size.longValue() != 16L*1024*1024*1024) {
			throw new IllegalStateException("Unexpected size: " + size);
		}
		blackhole.consume(size.longValue());
	}

Do you have a reproducer?

https://github.com/kvr000/zbynek-java-exp/tree/master/http-exp/http-benchmark

kvr000 avatar Nov 29 '25 01:11 kvr000

This is a known issue of the HttpClient API when invoking the client from a non Vert.x thread: https://github.com/eclipse-vertx/vert.x/pull/4705

We haven't moved forward to avoid breaking the API

We'll update the doc to warn about usage

tsegismont avatar Dec 01 '25 11:12 tsegismont