如何处理或避免BlockedIndefinitelyOnSTM异常? [英] How to handle or avoid BlockedIndefinitelyOnSTM exception?

查看:167
本文介绍了如何处理或避免BlockedIndefinitelyOnSTM异常?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我花了很多时间解决我在我正在处理的应用程序中遇到的问题。此应用程序是一个Web应用程序,使用scotty公开REST端点。它使用 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆