2017-09-15 40 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)) 

会是这样的工作?