Why is the following Slick database collection leak code?

I wrote the following shell for an Action game that will use a function that accepts both a session and a request. Here is the first version:

def ActionWithSession[A](bp: BodyParser[A])(f: Session => Request[A] => Result): Action[A] = Action(bp) { db.withSession { session: DbSession => request => f(session)(request) } } 

This version works well (the correct result is returned to the browser), however, each call will leak a connection to the database. After several calls, I started getting the following exceptions:

 java.sql.SQLException: Timed out waiting for a free available connection. 

When I change it to a lower version (by moving request => right after the action, the connection leak disappears and it works.

 def ActionWithSession[A](bp: BodyParser[A])(f: Session => Request[A] => Result): Action[A] = Action(bp) { request => db.withSession { session: DbSession => f(session)(request) } } 

Why does the first version cause a connection leak and how does the second version fix this?

+4
source share
2 answers

The first version of the code should not work. You should not return anything containing a reference to the Session object from the withSession scope. Here you return a closure that contains such a link. When the close is later called by Play, the sSession area is already closed and the Session object is invalid. Admittedly, leaking a Session object in a closure is very easy (and will be caught by Slick in the future).

That's why it seems to work at first, but the leak of Connection: Session objects lazily finds a connection. withSession blocks (or closes) the connection at the end of the block if it was acquired. However, when an unused Session object leaks from the block and is used for the first time after the block is completed, it still lazily opens the connection, but nothing automatically closes it. Some time ago, we recognized this as undesirable behavior, but have not yet corrected it. The fix we have in mind prohibits the Session object from receiving connections after calling their .close method. In your case, this would lead to an exception instead of a leak.

See https://github.com/slick/slick/pull/107

The correct code is indeed the second version you sent, where the returned closure element contains the entire withSession block, not just its result.

+4
source

db.withSession receives a function that receives Session in its first argument and executes it with some session that it provides. The return value of db.withSession is what this function returns.

In the first version, the expression passed using the session function is evaluated as the request => f(session)(request) function, so db.withSession ends: creating an instance of the session, creates an instance of the function object associated with this session, close the session (before how the function that she created is called!), and returns this associated function. Now Action received exactly what it needs - a function that accepts Request[A] and gives Result . However, at the time that Play will perform this action, the session will be lazily open, but there is nothing that will return its package to the pool.

The second version does it right, inside db.withSession we actually call f , and not return the functin that f calls. This ensures that the call to f nested inside db.withSession and occurs during the session.

Hope this helps someone!

+3
source

Source: https://habr.com/ru/post/1498304/


All Articles