bitemyapp / esqueleto

New home of Esqueleto, please file issues so we can get things caught up!
BSD 3-Clause "New" or "Revised" License
372 stars 107 forks source link

Unexpected Behaviour of `locking` #237

Closed jfraudeau closed 3 years ago

jfraudeau commented 3 years ago

What Happens: When using locking in a concurrent but single-threaded environment. A call to select with locking seems to be blocking as long as the lock is not granted.

What I Expect: The call to select behaves in a non-blocking fashion and allows concurrent user thread to continue their execution until the lock is granted. This is the behavior of update when it is unable to acquire a lock.

How to reproduce: See repo for a program that starts two concurrent transactions asynchronously, the committed code will block indefinitely. Toggling -threaded in the cabal and running with multiple OS Thread allows the two transactions to finish. Without -threaded commenting the call to lockRow terminates correctly.

Not sure if it is a bug or intended behavior, while using -threaded is a nice workaround I imagine OS Threads could also all end-up blocked.

belevy commented 3 years ago

I don't think this is a bug against esqueleto as the query it is generating seems to be correct. We use rawQuery from persistent under the hood so if anything this would be a bug against persistent-postgresql. That said this seems to be the expected behavior. You are requesting the query to grab a mutex to lock the row. Since the first thread has that lock you have to wait for the thread with the lock to release it before the select on the other thread completes

EDIT: Just saw the bit about threaded. The threaded runtime doesn't require multiple OS threads to work and should always be enabled.

jfraudeau commented 3 years ago

Thank you for your comment, I agree that the problem doesn't come from esqueleto but rather from persistent. I didn't think of rawSql and hence could only generate for update with esqueleto.

My surprise doesn't come from the fact that the second thread has to wait for the mutex, that's the reason for using locking but the fact that the wait is done with blocking IO while in Haskell and for non-select statements IO is usually non blocking.

PS: I tried running it with -threaded and +RTS -N1 and it does in fact terminate. I don't understand what it does vs non-threaded but I guess my problem is solved.