Allow serialization errors through internal error handling.

This commit is contained in:
Alexander K. Hudek 2014-12-02 13:14:18 -05:00
parent e67aa2508a
commit d88471409e
3 changed files with 22 additions and 10 deletions

View file

@ -1,4 +1,4 @@
(defproject views "0.4.3" (defproject views "0.4.4"
:description "You underestimate the power of the SQL side" :description "You underestimate the power of the SQL side"
:url "https://github.com/diligenceengine/views" :url "https://github.com/diligenceengine/views"

View file

@ -1,4 +1,5 @@
(ns views.db.deltas (ns views.db.deltas
(:import (java.sql SQLException))
(:require (:require
[clojure.string :refer [split]] [clojure.string :refer [split]]
[clojure.java.jdbc :as j] [clojure.java.jdbc :as j]
@ -8,7 +9,7 @@
[views.db.load :as vdbl] [views.db.load :as vdbl]
[views.db.checks :as vc] [views.db.checks :as vc]
[views.db.honeysql :as vh] [views.db.honeysql :as vh]
[views.db.util :refer [safe-map log-exception]])) [views.db.util :refer [safe-map log-exception serialization-error?]]))
;; ;;
;; Terminology and data structures used throughout this code ;; Terminology and data structures used throughout this code
@ -218,9 +219,10 @@
(try (try
(let [refresh-set (get (vdbl/initial-view db view-sig templates view) view-sig)] (let [refresh-set (get (vdbl/initial-view db view-sig templates view) view-sig)]
(update-in d [view-sig] (update-deltas-with-refresh-set refresh-set))) (update-in d [view-sig] (update-deltas-with-refresh-set refresh-set)))
(catch Exception e ;; ignore any failed view deltas ;; allow serialization errors
(log-exception e) (catch SQLException e (if (serialization-error? e) (throw e) d))
d))) ;; ignore any failed view deltas
(catch Exception e (log-exception e) d)))
deltas deltas
refresh-only-views)) refresh-only-views))

View file

@ -9,7 +9,7 @@
;; java.sql.SQLException: ERROR: could not serialize access due to concurrent update ;; java.sql.SQLException: ERROR: could not serialize access due to concurrent update
;; ;;
(defn get-nested-exceptions* (defn get-nested-exceptions*
[exceptions e] [exceptions ^SQLException e]
(if-let [next-e (.getNextException e)] (if-let [next-e (.getNextException e)]
(recur (conj exceptions next-e) next-e) (recur (conj exceptions next-e) next-e)
exceptions)) exceptions))
@ -19,6 +19,11 @@
[e] [e]
(get-nested-exceptions* [e] e)) (get-nested-exceptions* [e] e))
(defn serialization-error?
"True if e is a serialization error."
[^SQLException e]
(boolean (some #(= (.getSQLState ^SQLException %) "40001") (get-nested-exceptions e))))
;; TODO: update to avoid stack overflow. ;; TODO: update to avoid stack overflow.
(defn retry-on-transaction-failure (defn retry-on-transaction-failure
"Retry a function whenever we receive a transaction failure." "Retry a function whenever we receive a transaction failure."
@ -31,7 +36,7 @@
(debug "Exception message: " (.getMessage e)) (debug "Exception message: " (.getMessage e))
;; (debug "stack trace message: " (.printStackTrace e)) ;; (debug "stack trace message: " (.printStackTrace e))
(if (some #(= (.getSQLState %) "40001") (get-nested-exceptions e)) (if (serialization-error? e)
(retry-on-transaction-failure transaction-fn) ;; try it again (retry-on-transaction-failure transaction-fn) ;; try it again
(throw e))))) ;; otherwise rethrow (throw e))))) ;; otherwise rethrow
@ -42,7 +47,7 @@
(retry-on-transaction-failure tfn#))) (retry-on-transaction-failure tfn#)))
(defn log-exception (defn log-exception
[e] [^Exception e]
(error "views internal" (error "views internal"
(str (str
"e: " e "e: " e
@ -50,6 +55,11 @@
" trace: " (with-out-str (print-stack-trace e))))) " trace: " (with-out-str (print-stack-trace e)))))
(defn safe-map (defn safe-map
"A non-lazy map that skips any results that throw exeptions." "A non-lazy map that skips any results that throw exeptions other than SQL
serialization errors."
[f items] [f items]
(reduce #(try (conj %1 (f %2)) (catch Exception e %1)) [] items)) (reduce #(try (conj %1 (f %2))
(catch SQLException e (if (serialization-error? e) (throw e) %1))
(catch Exception e %1))
[]
items))