spring-projects / spring-boot

Spring Boot
https://spring.io/projects/spring-boot
Apache License 2.0
74.7k stars 40.58k forks source link

Database connection leak with SseEmitter #12319

Closed Oduig closed 6 years ago

Oduig commented 6 years ago

I have a problem with a database connection pool in Spring Boot 2, where connections are not returned to the pool even though they are neatly wrapped in @Transactional.

Upon opening the home page, I run a query and then open an SSE stream:

    @GetMapping("")
    public SseEmitter home() {
      derpService.derp();
      return obs.subscribeSse();
    }

The derp() call looks like this:

    @Transactional
    void derp() {
        derpRepository.derp();
    }

Which leads to:

    @Query(value = "SELECT 'derp'", nativeQuery = true)
    void derp();

As long as the SSE stream is open, the connection from the derp() call is not released. This goes against my intuition, because I assumed @Transactional would return the connection as soon as the flow exits the transaction scope.

Is this a bug in Spring?

Steps to reproduce

  1. Open the minimal example (link below)
  2. Run docker-compose up -d --build
  3. Start the application
  4. Open http://localhost:8088 in six tabs
  5. The sixth tab will not load because all connections are in use.

Resources

wilkinsona commented 6 years ago

In the interests of keeping the discussion in one place, the question on Stack Overflow is sufficient.

janickr commented 3 years ago

I experienced the same problem. I could not find anything in the spring and spring boot reference manual that warned against the use of @Transactional in combination with SSEEmitter and open entitymanager in view.

Unless I'm mistaken we could fix the issue in OpenEntityManagerInViewInterceptor and OpenSessionInViewInterceptor (and probably also the filters) by closing the entitymanager in afterConcurrentHandlingStarted (as is done in afterCompletion). I don't see any reason to keep the entitymanager open at the moment: is this a bug or is there a use case for this that I'm unaware of?

Oduig commented 3 years ago

@janickr This is closed, I found the solution myself and posted it on SO. Did you try adding spring.jpa.open-in-view=true to your application config?

janickr commented 3 years ago

@Oduig Hi thanks for the reply!

I know, I read the SO thread before I found this bug ticket. But only after spending more than a day figuring out why my connection pool kept exhausting after a while. IMO this is still a bug (unless I'm missing something about the nature of async web requests and entity managers)

The reason I commented on this ticket is this: it is really not that obvious that @Transactional in combination with spring.jpa.open-in-view and SseEmitter can lead to connection leaks. If this is expected (and intended) behaviour, it would be desirable that it is documented in the spring reference here : https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-async-sse (as such it is a documentation bug) If not, it is a bug in the framework code, and we should try to fix this. So that is why I wondered why afterConcurrentHandlingStarted would unbind the entityManager without actually closing it.

bclozel commented 3 years ago

@janickr feel free to contribute a PR to the Spring Framework project, or start a discussion with a new issue. I suspect that this will be a hard one to tackle since it's generally applicable to "you should not bind resources to a persistent connection/streaming responses if they're likely to deplete a pool of resources".

Divine1 commented 2 years ago

@bclozel this issue occurs without "binding resource to a persistent connection".

Divine1 commented 2 years ago

i'm also experiencing the same issue. i have no idea how to release the connection. as a temporary fix i have removed invoking database queries which creating sseemitter.

bclozel commented 2 years ago

If you’re starting a transaction, a streaming query or if the session is configured to stay opened with the view, you are effectively binding that database resource to the persistent connection. If you believe you’ve found a different problem, please create a new issue with a sample, minimal project that reproduces the behavior.

Divine1 commented 2 years ago

@bclozel thank you, i think its the same problem not different

spring.jpa.open-in-view = false

adding above property fixed the issue. it detaches the dbconnectionSession from the incoming session.

references

alohaaloha commented 1 month ago

spring.jpa.open-in-view = false is not the answer since majority of spring applications relies on lazy loading of entities and their fields. This means that keeping connection open is a bug when working with SseEmitter.