-
Notifications
You must be signed in to change notification settings - Fork 50
Prevent FdData leaks in getNotification #99
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
base: master
Are you sure you want to change the base?
Conversation
The rationale for this change is a memory leak observed in a production application using `postgresql-simple-0.6.4`. The related code is proprietary but I will try to give the gist of the issue. If a minimal repro is requested I could put one together. When `getNotification` is blocking in an `async`-spawned thread, upon cancelling and restarting the thread in a loop (reusing the same connection) a heap profile shows an increasing amount of `FdData` closures building up (along with a few other closures, e.g. the `TVar Nothing` created in `threadWaitSTM`). Forgive my limited knowledge of the GHC `EventManager`, but it seems that the registered callback made by `threadWaitReadSTM` does not get removed from the `EventManager` state when `waitRead` (I presume) is interrupted by the `AsyncCancelled`. This change somewhat mirrors how the non-STM `threadWait` handles exceptions, and so I think it should be benign. I've tested this change against our application and confirmed the `FdData` and related closures are no longer hanging around.
| (waitRead, unregister) <- threadWaitReadSTM fd | ||
| return $ do | ||
| atomically waitRead `catch` (throwIO . setIOErrorLocation) | ||
| mapException setIOErrorLocation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not
atomically waitRead `catch` \e -> do
unregister
throwIO (setIOErrorLocation e)or can unregister itself throw? Even then I wouldn't use mapException as it uses unsafePerformIO, and here it's not required.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tested your suggestion but saw no change in the heap, I think because it's not catching the async exception. I can still try to avoid mapException if you think it's that contentious (even though its documentation suggests it's safe).
Have you asked on GHC issue tracker, that sounds like a bug in EventManager. |
|
Also reproducer would be nice. If I understand right, just doing |
|
I also don't understand what we get by using Could you try your application with |
Indeed, avoiding Here's a minimal reproduction: https://github.com/velveteer/postgresql-simple-leak-repro |
The rationale for this change is a memory leak observed in a production
application using
postgresql-simple-0.6.4. The related code isproprietary but I will try to give the gist of the issue. If a minimal
repro is requested I could put one together.
When
getNotificationis blocking in anasync-spawned thread, uponcancelling and restarting the thread in a loop (reusing the same
connection) a heap profile shows an increasing amount of
FdDataclosures building up (along with a few other closures, e.g. the
TVar Nothingcreated inthreadWaitSTM).Forgive my limited knowledge of
the GHC
EventManager, but it seems that the registered callback madeby
threadWaitReadSTMdoes not get removed from theEventManagerstate when
waitRead(I presume) is interrupted by theAsyncCancelled.This change somewhat mirrors how the non-STM
threadWaithandlesexceptions, and so I think it should be benign. I've tested this change
against our application and confirmed the
FdDataand related closuresare no longer hanging around.