0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-02-13 10:38:13 -05:00

🎉 Add generic retry middleware for rpc methods.

This commit is contained in:
Andrey Antukh 2021-11-04 15:53:23 +01:00 committed by Andrés Moya
parent 6a6f88c6ef
commit bf2a393fd3
3 changed files with 52 additions and 20 deletions

View file

@ -14,6 +14,7 @@
[app.loggers.audit :as audit] [app.loggers.audit :as audit]
[app.metrics :as mtx] [app.metrics :as mtx]
[app.rlimits :as rlm] [app.rlimits :as rlm]
[app.util.retry :as retry]
[app.util.services :as sv] [app.util.services :as sv]
[clojure.spec.alpha :as s] [clojure.spec.alpha :as s]
[cuerdas.core :as str] [cuerdas.core :as str]
@ -92,6 +93,7 @@
(defn- wrap-impl (defn- wrap-impl
[{:keys [audit] :as cfg} f mdata] [{:keys [audit] :as cfg} f mdata]
(let [f (wrap-with-rlimits cfg f mdata) (let [f (wrap-with-rlimits cfg f mdata)
f (retry/wrap-retry cfg f mdata)
f (wrap-with-metrics cfg f mdata) f (wrap-with-metrics cfg f mdata)
spec (or (::sv/spec mdata) (s/spec any?)) spec (or (::sv/spec mdata) (s/spec any?))
auth? (:auth mdata true)] auth? (:auth mdata true)]
@ -99,7 +101,6 @@
(l/trace :action "register" :name (::sv/name mdata)) (l/trace :action "register" :name (::sv/name mdata))
(with-meta (with-meta
(fn [params] (fn [params]
;; Raise authentication error when rpc method requires auth but ;; Raise authentication error when rpc method requires auth but
;; no profile-id is found in the request. ;; no profile-id is found in the request.
(when (and auth? (not (uuid? (:profile-id params)))) (when (and auth? (not (uuid? (:profile-id params))))

View file

@ -12,6 +12,9 @@
[app.rpc.queries.comments :as comments] [app.rpc.queries.comments :as comments]
[app.rpc.queries.files :as files] [app.rpc.queries.files :as files]
[app.util.blob :as blob] [app.util.blob :as blob]
#_:clj-kondo/ignore
[app.util.retry :as retry]
[app.util.services :as sv] [app.util.services :as sv]
[app.util.time :as dt] [app.util.time :as dt]
[clojure.spec.alpha :as s])) [clojure.spec.alpha :as s]))
@ -32,6 +35,9 @@
(s/keys :req-un [::profile-id ::file-id ::position ::content ::page-id])) (s/keys :req-un [::profile-id ::file-id ::position ::content ::page-id]))
(sv/defmethod ::create-comment-thread (sv/defmethod ::create-comment-thread
{::retry/enabled true
::retry/max-retries 3
::retry/matches retry/conflict-db-insert?}
[{:keys [pool] :as cfg} {:keys [profile-id file-id] :as params}] [{:keys [pool] :as cfg} {:keys [profile-id file-id] :as params}]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(files/check-read-permissions! conn profile-id file-id) (files/check-read-permissions! conn profile-id file-id)
@ -43,7 +49,7 @@
res (db/exec-one! conn [sql file-id])] res (db/exec-one! conn [sql file-id])]
(:next-seqn res))) (:next-seqn res)))
(defn- create-comment-thread* (defn- create-comment-thread
[conn {:keys [profile-id file-id page-id position content] :as params}] [conn {:keys [profile-id file-id page-id position content] :as params}]
(let [seqn (retrieve-next-seqn conn file-id) (let [seqn (retrieve-next-seqn conn file-id)
now (dt/now) now (dt/now)
@ -78,24 +84,6 @@
(select-keys thread [:id :file-id :page-id]))) (select-keys thread [:id :file-id :page-id])))
(defn- create-comment-thread
[conn params]
(loop [sp (db/savepoint conn)
rc 0]
(let [res (ex/try (create-comment-thread* conn params))]
(cond
(and (instance? Throwable res)
(< rc 3))
(do
(db/rollback! conn sp)
(recur (db/savepoint conn)
(inc rc)))
(instance? Throwable res)
(throw res)
:else res))))
(defn- retrieve-page-name (defn- retrieve-page-name
[conn {:keys [file-id page-id]}] [conn {:keys [file-id page-id]}]
(let [{:keys [data]} (db/get-by-id conn :file file-id) (let [{:keys [data]} (db/get-by-id conn :file file-id)

View file

@ -0,0 +1,43 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) UXBOX Labs SL
(ns app.util.retry
"A fault tolerance helpers. Allow retry some operations that we know
we can retry."
(:require
[app.common.exceptions :as ex]
[app.common.logging :as l]
[app.util.async :as aa]
[app.util.services :as sv]))
(defn conflict-db-insert?
"Check if exception matches a insertion conflict on postgresql."
[e]
(and (instance? org.postgresql.util.PSQLException e)
(= "23505" (.getSQLState e))))
(defn wrap-retry
[_ f {:keys [::max-retries ::matches ::sv/name]
:or {max-retries 3
matches (constantly false)}
:as mdata}]
(when (::enabled mdata)
(l/debug :hint "wrapping retry" :name name))
(if (::enabled mdata)
(fn [cfg params]
(loop [retry 1]
(when (> retry 1)
(l/debug :hint "retrying controlled function" :retry retry :name name))
(let [res (ex/try (f cfg params))]
(if (ex/exception? res)
(if (and (matches res) (< retry max-retries))
(do
(aa/thread-sleep (* 100 retry))
(recur (inc retry)))
(throw res))
res))))
f))