Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebTestClient leaks when ParameterizedTypeReference is used #33389

Closed
MFAshby opened this issue Aug 14, 2024 · 0 comments
Closed

WebTestClient leaks when ParameterizedTypeReference is used #33389

MFAshby opened this issue Aug 14, 2024 · 0 comments
Assignees
Labels
in: test Issues in the test module in: web Issues in web modules (web, webmvc, webflux, websocket) type: bug A general bug
Milestone

Comments

@MFAshby
Copy link

MFAshby commented Aug 14, 2024

WebTestClient has a special case when a Void response is expected. Method WebTestClient.ResponseSpec#returnResult(java.lang.Class<T>) has this documentation comment:

Note that when {@code Void.class} is passed in, the response body is consumed and released.

The same special case logic is not applied to the overloaded version of this method: WebTestClient.ResponseSpec#returnResult(ParameterizedTypeReference<T>). This is a bit unexpected; and caused a memory leak in my tests when I swapped a Class parameter for a ParameterizedTypeReference parameter. The following sample code shows the problem (additional GC is forced to make the leak happen faster; netty's leak detection happens on GC):

import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.test.web.reactive.server.WebTestClient;
import reactor.netty.http.client.HttpClient;

class Scratch {
    public static void main(String[] args) throws Exception {
        var client = WebTestClient.bindToServer(new ReactorClientHttpConnector(HttpClient.create()))
                .build();
        var b = client.get().uri("http://localhost:8000");
        long count = 0;
        while(true) {
            count += 1;
            if (count % 10000 == 0) {
                System.out.printf("Done %d requests forcing a G\n", count);
                System.gc();
            }
            b.exchange()
                    .expectStatus().isEqualTo(200)
                    //.returnResult(Void.class);                                       // <-- doesn't leak
                    .returnResult(ParameterizedTypeReference.forType(Void.class)); // <-- leaks
        }
    }
}

The workaround is to use the Class signature, or consume the response body some other way using e.g. by specifying .consumeWith(...).

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Aug 14, 2024
@snicoll snicoll transferred this issue from spring-projects/spring-boot Aug 14, 2024
@sbrannen sbrannen added in: test Issues in the test module in: web Issues in web modules (web, webmvc, webflux, websocket) labels Aug 16, 2024
@sdeleuze sdeleuze added the status: waiting-for-internal-feedback An issue that needs input from a member or another Spring Team label Aug 16, 2024
@sdeleuze sdeleuze self-assigned this Aug 16, 2024
@sdeleuze sdeleuze added type: bug A general bug and removed status: waiting-for-triage An issue we've not yet triaged or decided on status: waiting-for-internal-feedback An issue that needs input from a member or another Spring Team labels Aug 16, 2024
@sdeleuze sdeleuze added this to the 6.1.13 milestone Aug 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: test Issues in the test module in: web Issues in web modules (web, webmvc, webflux, websocket) type: bug A general bug
Projects
None yet
Development

No branches or pull requests

4 participants