如何处理或避免BlockedIndefinitelyOnSTM异常? [英] How to handle or avoid BlockedIndefinitelyOnSTM exception?
问题描述
TVar
来保持其状态,该状态通过由前端层触发的 STM a
操作进行更新。 由于此应用程序基于事件源采购原则,STM事务完成后由业务层生成的任何事件都存储到 EventStore
(目前是一个简单的平面文件...)。以下是相关的代码片段:
newtype(EventStore m)=> WebStateM sma = WebStateM {runWebM :: ReaderT(TVar s)ma}
派生(Functor,Applicative,Monad,MonadIO,MonadTrans,MonadReader(TVar s))
applyCommand ::(EventStore m,Serializable(Event a))=>
命令a
- > TVar s
- > WebStateM s m(Event a)
applyCommand command = \ v - >做
(e,etype :: EventType s)< - liftIO $原子地$ actAndApply v
storeEvent e etype
返回e
其中
actAndApply = \ v - > do
s< - readTVar v
let view = getView s
let e = view`act` command
let bv = view`apply` e
modifyTVar'v(setView bv)
return(e,getType view)
这完美地工作,直到错误在 storeEvent
函数中滑动。这个函数负责将事件序列化为适当的类型,并且我在序列化例程中犯了一个(严重)错误,导致某种类型的错误,导致无限循环!然后突然间,我的 cabal test
开始挂起并失败,并超时(我使用wreq作为客户端库来测试REST服务)。我花了几个小时来确定服务器端的实际错误: tests:线程无限期地在STM事务中被阻塞
。怀疑系列化程序,我花了几个小时找出罪魁祸首并解决问题。
虽然我当然对错误负责(我应该更彻底地测试我的序列化例程!),但我发现它很具误导性。我想更好地了解这个错误来自哪里以及如何防止它。我已阅读爱德华杨的帖子,此邮件主题,但我必须承认导致观察此错误的逻辑链事件对我来说并不完全清楚。
我认为我理解调用 applyCommand
?)在评估 storeEvent
时启动,但我不明白这与交易是垃圾 有什么关系。 c> retry ,这会在事情发生变化时重新运行事务。但它等待修改的东西不再被引用,因此重试可以永远不会发生。这是一个错误。基本上这个线程现在挂了。
我想 c $ c> TVar ,但它因为异常而死亡,从而放弃了对 TVar
的最后一个引用并引发异常。
这就是我认为发生的事情。没有看到整个应用程序,很难确定。
I spent quite a lot of time troubleshooting an issue I had in the application I am working on. This application is a web app, exposing REST endpoints using scotty. It uses a TVar
to hold its state which is updated through STM a
actions triggered by the front-end layer.
As this application is based on event sourcing principles, any event generated by business layer after STM transactions complete is stored into an EventStore
(currently a simple flat file...). Here is the relevant code fragment:
newtype (EventStore m) => WebStateM s m a = WebStateM { runWebM :: ReaderT (TVar s) m a }
deriving (Functor,Applicative,Monad, MonadIO, MonadTrans, MonadReader (TVar s))
applyCommand :: (EventStore m, Serializable (Event a)) =>
Command a
-> TVar s
-> WebStateM s m (Event a)
applyCommand command = \ v -> do
(e, etype :: EventType s) <- liftIO $ atomically $ actAndApply v
storeEvent e etype
return e
where
actAndApply = \ v -> do
s <- readTVar v
let view = getView s
let e = view `act` command
let bv = view `apply` e
modifyTVar' v (setView bv)
return (e, getType view)
This works perfectly, until a bug slipped in the storeEvent
function. This function is responsible for serialising the event with the appropriate type, and I made a (gross) mistake in my serialisation routine for some type which lead to an infinite loop! Then all of a sudden, my cabal test
began to hang and fail with a timeout (I use wreq as client library to test REST services). It took me a couple of hours to pin down the actual error on the server side: tests: thread blocked indefinitely in an STM transaction
. Suspecting the serialisation routine, it took me another couple of hours to nail down the culprit and fix the issue.
Although I am of course entirely responsible for the error (I should have tested more thoroughly my serialisation routine!), I found it quite misleading. I would like to understand better where this error comes from and how to prevent it. I have read Edward Yang's post on the the subject, and this mail thread but I must confess the logical chain of events leading to observing this error is not entirely clear to me.
I think I understand the thread calling applyCommand
, which is spawned by scotty, dies from some exception (stack exhausted?) launched while evaluating storeEvent
, but I do not understand how this is related to the transaction being garbage.
The exception says that one thread tried to do a transaction, and hit retry
, which will rerun the transaction when something changes. But the thing it's waiting for changes on is no longer referenced anywhere, so the retry can never happen. And that's a bug. Basically that thread is hung now.
I would imagine that some thread somewhere was supposed to update this TVar
, but it died because of an exception, thereby dropping the last reference to that TVar
and provoking the exception.
That's what I think happened. Without seeing the entire application, it's difficult to be sure.
这篇关于如何处理或避免BlockedIndefinitelyOnSTM异常?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!