The timeout manager exception
The other day I bumped the dependencies of a Haskell project at work and noticed a new exception being thrown:
Thread killed by timeout manager
After a couple of false starts (it wasn't the connection pool, nor was it servant) I realised that a better approach would be to look at the list of packages that were updated as part of the dependency bumping.1 Most of them I thought would be very unlikely sources of it, but two in the list stood out:
Package | Pre | Post |
---|---|---|
unliftio | 0.2.14 | 0.2.18 |
warp | 3.3.15 | 3.3.16 |
warp
since the exception seemed to be thrown shortly after handling an HTTP
request, and unliftio
since the exception was caught by the handler for
uncaught exceptions and its description contains "thread". Also, when looking at
the code changes in warp
on GitHub2 I found that some of the changes
introduced was increased use of unliftio
for async stuff. The changes contain
mentions of TimeoutThread
and System.TimeManager
. That sounded promising,
and it lead me to the TimeoutThread exception in time-manager.
With that knowledge I could quickly adjust the handler for uncaught exceptions
to not log TimeoutThread
as fatal:
lastExceptionHandler :: LoggerSet -> SomeException -> IO () lastExceptionHandler logger e | Just TimeoutThread <- fromException e = return () | otherwise = do logFatalIoS logger $ pack $ "uncaught exception: " <> displayException e flushLogStr logger
I have to say it was a bit more work to arrive at this than I'd have liked. I reckon there are easier ways to track down the information I needed. So I'd love to hear what tricks and tips others have.
Footnotes:
As a bonus it gave me a good reason to reach for comm
, a command that I
rarely use but for some reason always enjoy.
GitHub's compare feature isn't very easy to discover, but a URL like this https://github.com/yesodweb/wai/compare/warp-3.3.15…warp-3.3.16 (note the 3 dots!) does the trick.