2017-09-15 69 views
3

我使用http請求一次從API中獲取數千個實體。作爲管道中的下一步,我想將它們全部鏟入數據庫中。使用線程宏的習慣性錯誤/異常處理

(->> ids 
    (pmap fetch-entity) 
    (pmap store-entity) 
    (doall)) 

fetch-entity期望一個String ID,並嘗試使用HTTP請求來檢索一個實體,並且要麼返回Map或拋出(因爲例如超時)的異常。

store-entity預計Map並嘗試將其存儲在數據庫中。它可能會引發異常(例如,如果Map與數據庫模式不匹配,或者它根本沒有收到Map)。

不雅錯誤處理

我的第一個「解決方案」是寫包裝函數fetch-entity'store-entity'趕上各自原有功能異常。

fetch-entity'在失敗時返回輸入,如果http請求失敗,基本上會傳遞String id。這確保整個管道保持卡車運輸。

store-entity'檢查其參數的類型。如果參數是Map(提取實體成功並返回Map),它會嘗試將其存儲在數據庫中。

如果存儲到數據庫的嘗試拋出一個異常,或者如果store-entity'得以通過一個String(ID),而不是Map它將conjerror_ids外部Vector

通過這種方式,我可以稍後使用error_ids來判斷出現故障的頻率以及哪些id受到影響。

它不覺得上述是一個明智的方式來實現我想要做的。例如,我寫store-entity'的方式完成了前一個管道步驟(fetch-entity')的功能,因爲它的行爲因上一個管道步驟是否成功而有所不同。

還有store-entity'請注意外部Vector被稱爲error_ids根本不覺得正確。

是否有一種慣用的方式來處理這種情況下,你有多個管道步驟,其中一些可以拋出異常(例如,因爲它們是I/O),我不能輕易使用謂詞來確保函數會表現出可預測性,並且我不想幹擾管道,只能稍後檢查哪些情況出錯?

+1

你看了看:http s://github.com/adambard/failjure? –

回答

4

它可以使用Try單子的類型,例如從cats library

它代表了計算,其可以任一導致異常或返回一個成功計算值。與任一monad非常相似,但在語義上不同。

它由兩種類型組成:成功和失敗。成功類型是一個簡單的包裝,就像任何一個monad的權利。但是Failure類型與Left稍有不同,因爲它總是包裝Throwable的實例(或者在cljs中包含任何值,因爲您可以在JavaScript主機中拋出任意值)。

(...)

它是try-catch模塊的一個類似物:它用基於堆的錯誤處理替換了try-catch的基於堆棧的錯誤處理。它沒有拋出異常並且不得不立即在同一個線程中處理它,它將斷開錯誤處理和恢復。

基於堆的錯誤處理是你想要的。

下面我做了一個fetch-entitystore-entity的例子。我做了fetch-entity在第一個id(1)上拋出ExceptionInfostore-entity在第二個id(0)上拋出DivideByZeroException

(ns your-project.core 
    (:require [cats.core :as cats] 
      [cats.monad.exception :as exc])) 


(def ids [1 0 2]) ;; `fetch-entity` throws on 1, `store-entity` on 0, 2 works 


(defn fetch-entity 
    "Throws an exception when the id is 1..." 
    [id] 
    (if (= id 1) 
    (throw (ex-info "id is 1, help!" {:id id})) 
    id)) 


(defn store-entity 
    "Unfortunately this function still needs to be aware that it receives a Try. 
    It throws a `DivideByZeroException` when the id is 0" 
    [id-try] 
    (if (exc/success? id-try)     ; was the previous step a success? 
    (exc/try-on (/ 1 (exc/extract id-try))) ; if so: extract, apply fn, and rewrap 
    id-try))        ; else return original for later processing 


(def results 
    (->> ids 
     (pmap #(exc/try-on (fetch-entity %))) 
     (pmap store-entity))) 

現在你可以篩選成功或失敗results分別success?failure?,並通過cats-extract

(def successful-results 
    (->> results 
     (filter exc/success?) 
     (mapv cats/extract))) 

successful-results ;; => [1/2] 


(def error-messages 
    (->> results 
     (filter exc/failure?) 
     (mapv cats/extract) ; gets exceptions without raising them 
     (mapv #(.getMessage %)))) 

error-messages ;; => ["id is 1, help!" "Divide by zero"] 

注意檢索值,如果你只想環比errorssuccessful-results一旦你能使用傳感器如下:

(transduce (comp 
      (filter exc/success?) 
      (map cats/extract)) 
      conj 
      results)) 
;; => [1/2] 
1

我首先想到的是fetch-entitystore-entity合併成一個單一的操作:

(defn fetch-and-store [id] 
    (try 
    (store-entity (fetch-entity id)) 
    (catch ... <log error msg>))) 

(doall (pmap fetch-and-store ids)) 

會是這樣的工作?