我有一个小程序,它应该逐个读取SQL查询/命令并对数据库执行它们。
如果查询成功执行,则执行下一个查询。 如果执行一个查询时出错,程序应该停止一起执行。
我有代码,但即使存在异常,查询仍会继续执行。
(defn main
[]
(loop [queries (get-all-queries)
querycount 1]
(let [q (first queries)]
(println (format "currently processing query %s", querycount))
(cond (nil? q) (println "All Queries ran successfully.")
:else (do
(cond (= (:status (process-query q querycount)) "OK")
(recur (rest queries) (+querycount 1)))
:else (println "An error occured while running queries")))))))
(defn process-query
[query query-count]
(let [{query-body :query-body, is-query-running? :is-query-running?} query
my-agent (agent
{:error false, :query-count query-count}
:error-handler handler-fn)]
(send my-agent (fn[_]
(execute-query! db query-body)))))
(loop [is-query-running? (is-query-running?)
error? (:error @my-agent)]
(cond error? (do (println "Error")
{:status "ERROR" :error-msg (:error-msg @my-agent)})
(and (not is-query-running?) (not error?)) (do (println "Success")
{:status "OK"})
(:else (do
(Thread/sleep 2000)
(recur (is-query-running?) (:error @my-agent)))))))
(defn handler-fn
[agent exception]
(println (format "an exception occured : %s" exception))
(if (instance? java.sql.BatchUpdateException exception)
(println (.getNextException exception)))
(send agent (? [_] {:error true, :error-message exception}))
(throw exception))
我使用代理的原因是我有一些需要4个小时才能运行的查询。 当发生这种情况时,数据库不会通知程序查询已完成。相反,该程序被卡住了。所以,相反,我不断轮询以检查查询是否已经完成。
答案 0 :(得分:1)
我认为你需要使用core.async来解决这种工作流程
看看http://clojure.com/blog/2013/06/28/clojure-core-async-channels.html
此lib将允许您使用相关的异步任务检查您的条件
一些可以帮助您的资源
http://www.infoq.com/news/2013/07/core-async
https://www.youtube.com/watch?v=AhxcGGeh5ho
答案 1 :(得分:1)
主要问题似乎是:一方面,你写的是长查询永远不会返回,即它们甚至不会抛出异常。另一方面,代理的错误检测机制基于捕获异常。
我认为您需要做的不是(主要)检查是否捕获到异常,而是execute-query
在is-query-running?
返回false时是否实际返回了有效结果。
关于正确的并发原语,我建议使用future而不是agent。它们比代理更简单,因为它们只能返回单个值,而不是多次更改它们的状态,而它们的错误处理方式是简单地返回异常而不是常规返回值。
然后,您可以遵循以下实现方法:在循环中,在将来执行带有超时的deref
。如果deref
的返回值是execute-query!
定期返回的值,则返回"OK"
(例如,将第二个表达式添加到future
正文作为明确可识别的返回值,例如关键字:ok
)。否则,如果deref
的返回值是一个例外,请像现在一样从异常中返回"ERROR"
:error-msg
。最后,如果返回值是您为deref
提供的超时值,请致电is-query-running?
。如果是,则循环另一个时间,如果它为假,则返回ERROR
一个特殊的:error-msg
,它传达您的查询结束而不返回也不抛出异常。 (并且可能会调用future-cancel
,因此您不会泄漏永无休止的execute-query!
调用的线程。)