2014-09-30 70 views
4

我花了很多時間解決我在我正在處理的應用程序中遇到的問題。此應用程序是一個Web應用程序,使用scotty公開REST端點。它使用TVar來保持其狀態,該狀態通過由前端層觸發的STM a動作更新。如何處理或避免BlockedIndefinitelyOnSTM異常?

由於這個應用程序是基於事件的採購原則,經過 STM交易完成由業務層產生的任何事件被存儲到EventStore(目前是一個簡單的平面文件...)。下面是相關的代碼片段:

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) 

這完美的作品,直到一個錯誤在storeEvent功能下滑。這個函數負責將事件序列化爲適當的類型,並且我在序列化例程中犯了一個(嚴重)錯誤,導致某種類型的錯誤,導致無限循環!然後突然間,我的cabal test開始掛起,並失敗​​(超時)(我使用wreq作爲客戶端庫來測試REST服務)。我花了幾個小時來確定服務器端的實際錯誤:tests: thread blocked indefinitely in an STM transaction。懷疑係列化程序,我花了幾個小時找出罪魁禍首並解決問題。

雖然我當然完全對錯誤負責(我應該更徹底地測試我的序列化例程!),但我發現它很具誤導性。我想更好地瞭解這個錯誤來自哪裏以及如何防止它。我已閱讀Edward Yang's帖子上的主題,而this mail thread但我必須承認導致觀察這個錯誤的邏輯鏈條並不完全清楚。

我想我明白了線程調用applyCommand,這是由斯科蒂催生,從一些異常(堆棧用盡?),而評估storeEvent推出死了,但我不明白這是怎麼與交易相關的是垃圾

回答

3

例外情況一個線程試圖執行一個事務,並且命中retry,這會在事情發生變化時重新運行事務。但它正在等待更改的東西不再被引用到任何地方,因此重試可能會發生從不發生。這是一個錯誤。基本上這個線程現在掛起。

我會想象一些線程某處是應該更新此TVar,但它死了,因爲異常的,從而下探的最後一個引用到TVar和挑釁除外。

這就是我認爲發生。沒有看到整個應用程序,很難確定。

+0

感謝您的洞察力。我可以理解,如果'主'線程死亡,那麼'TVar'不再被引用,但是我不明白'重試'應該是在哪裏。我不明確地在我的代碼中使用'retry',所以這必須是隱含的... – insitu 2014-09-30 12:47:24

+0

你使用'TChan'還是什麼?這將在內部使用'retry'來阻塞,直到通道中有數據可用。 – MathematicalOrchid 2014-09-30 13:58:48

+0

這就是令我困惑的原因:我不使用TChan,只使用readTVar,writeTVar,modifyTVar。 – insitu 2014-09-30 14:38:43