0

SQLクエリ/コマンドを1つずつ読み取り、データベースに対して実行する小さなプログラムがあります。

クエリが正常に実行されると、次のクエリが実行されます。1 つのクエリの実行中にエラーが発生した場合、プログラムはすべて一緒に実行を停止する必要があります。

例外が発生した場合でもクエリが引き続き実行されることを除いて、コードがあります。

(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 時間かかるクエリがあるためです。その場合、データベースはクエリが完了したことをプログラムに通知しません。代わりに、プログラムが動かなくなります。そのため、代わりに、クエリが既に実行されているかどうかを確認するために常にポーリングしています。

  • これは私がやろうとしていることを達成するための最良の方法ですか?
  • 他の同時実行プリミティブを使用する必要がありますか?
  • 同時実行プリミティブも必要ですか?
  • 私はこれについて長い間考えてきました。
4

2 に答える 2

1

主な問題は次のように思われます: 一方では、長いクエリが返されない、つまり、例外をスローしないと書いています。一方、エージェントのエラー検出メカニズムは、例外のキャッチに基づいています。

あなたがする必要があるのは、例外がキャッチされたかどうかを(主に)チェックすることではなく、falseを返したexecute-queryときに実際に有効な結果を返したかどうかをチェックすることだと思います。is-query-running?

適切な同時実行プリミティブに関しては、エージェントの代わりにフューチャーを使用することをお勧めします。状態を複数回変更するのではなく、単一の値のみを返すことができ、エラー処理の方法は、通常の戻り値の代わりに例外を返すだけであるため、エージェントよりも単純です。

次に、この実装のアイデアに従うことができます: ループ内でderef、将来のタイムアウトを実行します。の戻り値が定期的に返さderefれるものである場合は、戻ります(resp. 明確に識別可能な戻り値として本体に 2 番目の式を追加します。たとえば、キーワード)。それ以外の場合、 の戻り値が例外の場合は、今と同じように例外から戻ります。最後に、戻り値が に指定したタイムアウト値である場合は、 を呼び出します。true の場合はもう一度ループし、false の場合は、例外を返したりスローしたりせずにクエリが終了したことを伝えるスペシャルを返します。(そしておそらく電話するexecute-query!"OK"future:okderef"ERROR":error-msgderefis-query-running?ERROR:error-msgfuture-cancelexecute-query!そのため、終わりのない呼び出しのスレッドがリークすることはありません。)

于 2013-11-12T15:14:03.833 に答える