mirror of
https://github.com/penpot/penpot.git
synced 2025-05-10 20:46:37 +02:00
🎉 Add prometheus metrics.
This commit is contained in:
parent
ccb79f7188
commit
b532e74310
20 changed files with 359 additions and 82 deletions
|
@ -9,11 +9,15 @@
|
||||||
|
|
||||||
;; Logging
|
;; Logging
|
||||||
org.clojure/tools.logging {:mvn/version "1.1.0"}
|
org.clojure/tools.logging {:mvn/version "1.1.0"}
|
||||||
org.apache.logging.log4j/log4j-api {:mvn/version "2.13.2"}
|
org.apache.logging.log4j/log4j-api {:mvn/version "2.13.3"}
|
||||||
org.apache.logging.log4j/log4j-core {:mvn/version "2.13.2"}
|
org.apache.logging.log4j/log4j-core {:mvn/version "2.13.3"}
|
||||||
org.apache.logging.log4j/log4j-web {:mvn/version "2.13.2"}
|
org.apache.logging.log4j/log4j-web {:mvn/version "2.13.3"}
|
||||||
org.apache.logging.log4j/log4j-jul {:mvn/version "2.13.2"}
|
org.apache.logging.log4j/log4j-jul {:mvn/version "2.13.3"}
|
||||||
org.apache.logging.log4j/log4j-slf4j-impl {:mvn/version "2.13.2"}
|
org.apache.logging.log4j/log4j-slf4j-impl {:mvn/version "2.13.3"}
|
||||||
|
|
||||||
|
io.prometheus/simpleclient {:mvn/version "0.9.0"}
|
||||||
|
io.prometheus/simpleclient_hotspot {:mvn/version "0.9.0"}
|
||||||
|
io.prometheus/simpleclient_httpserver {:mvn/version "0.9.0"}
|
||||||
|
|
||||||
expound/expound {:mvn/version "0.8.4"}
|
expound/expound {:mvn/version "0.8.4"}
|
||||||
instaparse/instaparse {:mvn/version "1.4.10"}
|
instaparse/instaparse {:mvn/version "1.4.10"}
|
||||||
|
@ -26,7 +30,7 @@
|
||||||
seancorfield/next.jdbc {:mvn/version "1.0.424"}
|
seancorfield/next.jdbc {:mvn/version "1.0.424"}
|
||||||
metosin/reitit-ring {:mvn/version "0.4.2"}
|
metosin/reitit-ring {:mvn/version "0.4.2"}
|
||||||
org.postgresql/postgresql {:mvn/version "42.2.12"}
|
org.postgresql/postgresql {:mvn/version "42.2.12"}
|
||||||
com.zaxxer/HikariCP {:mvn/version "3.4.3"}
|
com.zaxxer/HikariCP {:mvn/version "3.4.5"}
|
||||||
|
|
||||||
funcool/datoteka {:mvn/version "1.2.0"}
|
funcool/datoteka {:mvn/version "1.2.0"}
|
||||||
funcool/promesa {:mvn/version "5.1.0"}
|
funcool/promesa {:mvn/version "5.1.0"}
|
||||||
|
@ -51,7 +55,7 @@
|
||||||
io.aviso/pretty {:mvn/version "0.1.37"}
|
io.aviso/pretty {:mvn/version "0.1.37"}
|
||||||
|
|
||||||
mount/mount {:mvn/version "0.1.16"}
|
mount/mount {:mvn/version "0.1.16"}
|
||||||
environ/environ {:mvn/version "1.1.0"}}
|
environ/environ {:mvn/version "1.2.0"}}
|
||||||
:paths ["src" "resources" "../common" "common"]
|
:paths ["src" "resources" "../common" "common"]
|
||||||
:aliases
|
:aliases
|
||||||
{:dev
|
{:dev
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Configuration status="info" monitorInterval="60">
|
<Configuration status="info" monitorInterval="60">
|
||||||
<Appenders>
|
<Appenders>
|
||||||
<Console name="console" target="SYSTEM_OUT">
|
<Console name="console" target="SYSTEM_OUT">
|
||||||
|
|
|
@ -17,11 +17,13 @@
|
||||||
[next.jdbc.result-set :as jdbc-rs]
|
[next.jdbc.result-set :as jdbc-rs]
|
||||||
[next.jdbc.sql :as jdbc-sql]
|
[next.jdbc.sql :as jdbc-sql]
|
||||||
[next.jdbc.sql.builder :as jdbc-bld]
|
[next.jdbc.sql.builder :as jdbc-bld]
|
||||||
|
[uxbox.metrics :as mtx]
|
||||||
[uxbox.common.exceptions :as ex]
|
[uxbox.common.exceptions :as ex]
|
||||||
[uxbox.config :as cfg]
|
[uxbox.config :as cfg]
|
||||||
[uxbox.util.data :as data])
|
[uxbox.util.data :as data])
|
||||||
(:import
|
(:import
|
||||||
org.postgresql.util.PGobject
|
org.postgresql.util.PGobject
|
||||||
|
com.zaxxer.hikari.metrics.prometheus.PrometheusMetricsTrackerFactory
|
||||||
com.zaxxer.hikari.HikariConfig
|
com.zaxxer.hikari.HikariConfig
|
||||||
com.zaxxer.hikari.HikariDataSource))
|
com.zaxxer.hikari.HikariDataSource))
|
||||||
|
|
||||||
|
@ -30,17 +32,20 @@
|
||||||
(let [dburi (:database-uri cfg)
|
(let [dburi (:database-uri cfg)
|
||||||
username (:database-username cfg)
|
username (:database-username cfg)
|
||||||
password (:database-password cfg)
|
password (:database-password cfg)
|
||||||
config (HikariConfig.)]
|
config (HikariConfig.)
|
||||||
|
mfactory (PrometheusMetricsTrackerFactory. mtx/registry)]
|
||||||
(doto config
|
(doto config
|
||||||
(.setJdbcUrl (str "jdbc:" dburi))
|
(.setJdbcUrl (str "jdbc:" dburi))
|
||||||
|
(.setPoolName "main")
|
||||||
(.setAutoCommit true)
|
(.setAutoCommit true)
|
||||||
(.setReadOnly false)
|
(.setReadOnly false)
|
||||||
(.setConnectionTimeout 30000)
|
(.setConnectionTimeout 30000) ;; 30seg
|
||||||
(.setValidationTimeout 5000)
|
(.setValidationTimeout 5000) ;; 5seg
|
||||||
(.setIdleTimeout 600000)
|
(.setIdleTimeout 900000) ;; 15min
|
||||||
(.setMaxLifetime 1800000)
|
(.setMaxLifetime 900000) ;; 15min
|
||||||
(.setMinimumIdle 10)
|
(.setMinimumIdle 5)
|
||||||
(.setMaximumPoolSize 20))
|
(.setMaximumPoolSize 10)
|
||||||
|
(.setMetricsTrackerFactory mfactory))
|
||||||
(when username (.setUsername config username))
|
(when username (.setUsername config username))
|
||||||
(when password (.setPassword config password))
|
(when password (.setPassword config password))
|
||||||
config))
|
config))
|
||||||
|
@ -127,3 +132,11 @@
|
||||||
(= typ "jsonb"))
|
(= typ "jsonb"))
|
||||||
(json/read-str val)
|
(json/read-str val)
|
||||||
val)))
|
val)))
|
||||||
|
|
||||||
|
;; Instrumentation
|
||||||
|
|
||||||
|
(mtx/instrument-with-counter!
|
||||||
|
{:var [#'jdbc/execute-one!
|
||||||
|
#'jdbc/execute!]
|
||||||
|
:id "database__query_counter"
|
||||||
|
:help "An absolute counter of database queries."})
|
||||||
|
|
|
@ -17,12 +17,14 @@
|
||||||
[uxbox.http.middleware :as middleware]
|
[uxbox.http.middleware :as middleware]
|
||||||
[uxbox.http.session :as session]
|
[uxbox.http.session :as session]
|
||||||
[uxbox.http.ws :as ws]
|
[uxbox.http.ws :as ws]
|
||||||
|
[uxbox.metrics :as mtx]
|
||||||
[uxbox.services.notifications :as usn]))
|
[uxbox.services.notifications :as usn]))
|
||||||
|
|
||||||
(defn- create-router
|
(defn- create-router
|
||||||
[]
|
[]
|
||||||
(rring/router
|
(rring/router
|
||||||
[["/api" {:middleware [[middleware/format-response-body]
|
[["/metrics" {:get mtx/dump}]
|
||||||
|
["/api" {:middleware [[middleware/format-response-body]
|
||||||
[middleware/errors errors/handle]
|
[middleware/errors errors/handle]
|
||||||
[middleware/parse-request-body]
|
[middleware/parse-request-body]
|
||||||
[middleware/params]
|
[middleware/params]
|
||||||
|
@ -37,7 +39,6 @@
|
||||||
["/logout" {:handler handlers/logout-handler
|
["/logout" {:handler handlers/logout-handler
|
||||||
:method :post}]
|
:method :post}]
|
||||||
|
|
||||||
|
|
||||||
["/w" {:middleware [session/auth]}
|
["/w" {:middleware [session/auth]}
|
||||||
["/query/:type" {:get handlers/query-handler}]
|
["/query/:type" {:get handlers/query-handler}]
|
||||||
["/mutation/:type" {:post handlers/mutation-handler}]]]]))
|
["/mutation/:type" {:post handlers/mutation-handler}]]]]))
|
||||||
|
@ -46,8 +47,9 @@
|
||||||
:start (rring/ring-handler
|
:start (rring/ring-handler
|
||||||
(create-router)
|
(create-router)
|
||||||
(constantly {:status 404, :body ""})
|
(constantly {:status 404, :body ""})
|
||||||
{:middleware [middleware/development-resources
|
{:middleware [[middleware/development-resources]
|
||||||
middleware/development-cors]}))
|
[middleware/development-cors]
|
||||||
|
[middleware/metrics]]}))
|
||||||
|
|
||||||
(defn start-server
|
(defn start-server
|
||||||
[cfg app]
|
[cfg app]
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
(:require
|
(:require
|
||||||
[clojure.tools.logging :as log]
|
[clojure.tools.logging :as log]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
|
[uxbox.metrics :as mtx]
|
||||||
[io.aviso.exception :as e]))
|
[io.aviso.exception :as e]))
|
||||||
|
|
||||||
(defmulti handle-exception
|
(defmulti handle-exception
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
[ring.middleware.multipart-params :refer [wrap-multipart-params]]
|
[ring.middleware.multipart-params :refer [wrap-multipart-params]]
|
||||||
[ring.middleware.params :refer [wrap-params]]
|
[ring.middleware.params :refer [wrap-params]]
|
||||||
[ring.middleware.resource :refer [wrap-resource]]
|
[ring.middleware.resource :refer [wrap-resource]]
|
||||||
|
[uxbox.metrics :as mtx]
|
||||||
[uxbox.common.exceptions :as ex]
|
[uxbox.common.exceptions :as ex]
|
||||||
[uxbox.config :as cfg]
|
[uxbox.config :as cfg]
|
||||||
[uxbox.util.transit :as t]))
|
[uxbox.util.transit :as t]))
|
||||||
|
@ -83,6 +84,12 @@
|
||||||
{:name ::errors
|
{:name ::errors
|
||||||
:compile (constantly wrap-errors)})
|
:compile (constantly wrap-errors)})
|
||||||
|
|
||||||
|
(def metrics
|
||||||
|
{:name ::metrics
|
||||||
|
:wrap (fn [handler]
|
||||||
|
(mtx/wrap-counter handler {:id "http__requests_counter"
|
||||||
|
:help "Absolute http requests counter."}))})
|
||||||
|
|
||||||
(def cookies
|
(def cookies
|
||||||
{:name ::cookies
|
{:name ::cookies
|
||||||
:compile (constantly wrap-cookies)})
|
:compile (constantly wrap-cookies)})
|
||||||
|
|
181
backend/src/uxbox/metrics.clj
Normal file
181
backend/src/uxbox/metrics.clj
Normal file
|
@ -0,0 +1,181 @@
|
||||||
|
;; 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/.
|
||||||
|
;;
|
||||||
|
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
|
;; defined by the Mozilla Public License, v. 2.0.
|
||||||
|
;;
|
||||||
|
;; Copyright (c) 2020 UXBOX Labs SL
|
||||||
|
|
||||||
|
(ns uxbox.metrics
|
||||||
|
(:require
|
||||||
|
[clojure.tools.logging :as log]
|
||||||
|
[cuerdas.core :as str])
|
||||||
|
(:import
|
||||||
|
io.prometheus.client.CollectorRegistry
|
||||||
|
io.prometheus.client.Counter
|
||||||
|
io.prometheus.client.Gauge
|
||||||
|
io.prometheus.client.Summary
|
||||||
|
io.prometheus.client.exporter.common.TextFormat
|
||||||
|
io.prometheus.client.hotspot.DefaultExports
|
||||||
|
java.io.StringWriter))
|
||||||
|
|
||||||
|
(defn- create-registry
|
||||||
|
[]
|
||||||
|
(let [registry (CollectorRegistry.)]
|
||||||
|
(DefaultExports/register registry)
|
||||||
|
registry))
|
||||||
|
|
||||||
|
(defonce registry (create-registry))
|
||||||
|
(defonce cache (atom {}))
|
||||||
|
|
||||||
|
(defmacro with-measure
|
||||||
|
[sym expr teardown]
|
||||||
|
`(let [~sym (System/nanoTime)]
|
||||||
|
(try
|
||||||
|
~expr
|
||||||
|
(finally
|
||||||
|
(let [~sym (/ (- (System/nanoTime) ~sym) 1000000)]
|
||||||
|
~teardown)))))
|
||||||
|
|
||||||
|
(defn make-counter
|
||||||
|
[{:keys [id help] :as props}]
|
||||||
|
(let [instance (doto (Counter/build)
|
||||||
|
(.name id)
|
||||||
|
(.help help))
|
||||||
|
instance (.register instance registry)]
|
||||||
|
(reify
|
||||||
|
clojure.lang.IDeref
|
||||||
|
(deref [_] instance)
|
||||||
|
|
||||||
|
clojure.lang.IFn
|
||||||
|
(invoke [_ cmd]
|
||||||
|
(.inc ^Counter instance))
|
||||||
|
|
||||||
|
(invoke [_ cmd val]
|
||||||
|
(case cmd
|
||||||
|
:wrap (fn
|
||||||
|
([a]
|
||||||
|
(.inc ^Counter instance)
|
||||||
|
(val a))
|
||||||
|
([a b]
|
||||||
|
(.inc ^Counter instance)
|
||||||
|
(val a b))
|
||||||
|
([a b c]
|
||||||
|
(.inc ^Counter instance)
|
||||||
|
(val a b c)))
|
||||||
|
|
||||||
|
(throw (IllegalArgumentException. "invalid arguments")))))))
|
||||||
|
|
||||||
|
(defn counter
|
||||||
|
[{:keys [id] :as props}]
|
||||||
|
(or (get @cache id)
|
||||||
|
(let [v (make-counter props)]
|
||||||
|
(swap! cache assoc id v)
|
||||||
|
v)))
|
||||||
|
|
||||||
|
(defn make-gauge
|
||||||
|
[{:keys [id help] :as props}]
|
||||||
|
(let [instance (doto (Gauge/build)
|
||||||
|
(.name id)
|
||||||
|
(.help help))
|
||||||
|
instance (.register instance registry)]
|
||||||
|
(reify
|
||||||
|
clojure.lang.IDeref
|
||||||
|
(deref [_] instance)
|
||||||
|
|
||||||
|
clojure.lang.IFn
|
||||||
|
(invoke [_ cmd]
|
||||||
|
(case cmd
|
||||||
|
:inc (.inc ^Gauge instance)
|
||||||
|
:dec (.dec ^Gauge instance))))))
|
||||||
|
|
||||||
|
(defn gauge
|
||||||
|
[{:keys [id] :as props}]
|
||||||
|
(or (get @cache id)
|
||||||
|
(let [v (make-gauge props)]
|
||||||
|
(swap! cache assoc id v)
|
||||||
|
v)))
|
||||||
|
|
||||||
|
(defn make-summary
|
||||||
|
[{:keys [id help] :as props}]
|
||||||
|
(let [instance (doto (Summary/build)
|
||||||
|
(.name id)
|
||||||
|
(.help help)
|
||||||
|
(.quantile 0.5 0.05)
|
||||||
|
(.quantile 0.9 0.01)
|
||||||
|
(.quantile 0.99 0.001))
|
||||||
|
instance (.register instance registry)]
|
||||||
|
(reify
|
||||||
|
clojure.lang.IDeref
|
||||||
|
(deref [_] instance)
|
||||||
|
|
||||||
|
clojure.lang.IFn
|
||||||
|
(invoke [_ val]
|
||||||
|
(.observe ^Summary instance val))
|
||||||
|
|
||||||
|
(invoke [_ cmd val]
|
||||||
|
(case cmd
|
||||||
|
:wrap (fn
|
||||||
|
([a]
|
||||||
|
(with-measure $$
|
||||||
|
(val a)
|
||||||
|
(.observe ^Summary instance $$)))
|
||||||
|
([a b]
|
||||||
|
(with-measure $$
|
||||||
|
(val a b)
|
||||||
|
(.observe ^Summary instance $$)))
|
||||||
|
([a b c]
|
||||||
|
(with-measure $$
|
||||||
|
(val a b c)
|
||||||
|
(.observe ^Summary instance $$))))
|
||||||
|
|
||||||
|
(throw (IllegalArgumentException. "invalid arguments")))))))
|
||||||
|
|
||||||
|
(defn summary
|
||||||
|
[{:keys [id] :as props}]
|
||||||
|
(or (get @cache id)
|
||||||
|
(let [v (make-summary props)]
|
||||||
|
(swap! cache assoc id v)
|
||||||
|
v)))
|
||||||
|
|
||||||
|
(defn wrap-summary
|
||||||
|
[f props]
|
||||||
|
(let [sm (summary props)]
|
||||||
|
(sm :wrap f)))
|
||||||
|
|
||||||
|
(defn wrap-counter
|
||||||
|
[f props]
|
||||||
|
(let [cnt (counter props)]
|
||||||
|
(cnt :wrap f)))
|
||||||
|
|
||||||
|
(defn instrument-with-counter!
|
||||||
|
[{:keys [var] :as props}]
|
||||||
|
(let [cnt (counter props)
|
||||||
|
vars (if (var? var) [var] var)]
|
||||||
|
(doseq [var vars]
|
||||||
|
(alter-var-root var (fn [root]
|
||||||
|
(let [mdata (meta root)
|
||||||
|
original (::counter-original mdata root)]
|
||||||
|
(with-meta
|
||||||
|
(cnt :wrap original)
|
||||||
|
(assoc mdata ::counter-original original))))))))
|
||||||
|
|
||||||
|
(defn instrument-with-summary!
|
||||||
|
[{:keys [var] :as props}]
|
||||||
|
(let [sm (summary props)]
|
||||||
|
(alter-var-root var (fn [root]
|
||||||
|
(let [mdata (meta root)
|
||||||
|
original (::summary-original mdata root)]
|
||||||
|
(with-meta
|
||||||
|
(sm :wrap original)
|
||||||
|
(assoc mdata ::summary-original original)))))))
|
||||||
|
|
||||||
|
(defn dump
|
||||||
|
[& args]
|
||||||
|
(let [samples (.metricFamilySamples ^CollectorRegistry registry)
|
||||||
|
writer (StringWriter.)]
|
||||||
|
(TextFormat/write004 writer samples)
|
||||||
|
{:headers {"content-type" TextFormat/CONTENT_TYPE_004}
|
||||||
|
:body (.toString writer)}))
|
||||||
|
|
73
backend/src/uxbox/services/middleware.clj
Normal file
73
backend/src/uxbox/services/middleware.clj
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
;; 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/.
|
||||||
|
;;
|
||||||
|
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
|
;; defined by the Mozilla Public License, v. 2.0.
|
||||||
|
;;
|
||||||
|
;; Copyright (c) 2020 UXBOX Labs SL
|
||||||
|
|
||||||
|
(ns uxbox.services.middleware
|
||||||
|
"Common middleware for services."
|
||||||
|
(:require
|
||||||
|
[clojure.tools.logging :as log]
|
||||||
|
[clojure.spec.alpha :as s]
|
||||||
|
[cuerdas.core :as str]
|
||||||
|
[expound.alpha :as expound]
|
||||||
|
[uxbox.common.exceptions :as ex]
|
||||||
|
[uxbox.common.spec :as us]
|
||||||
|
[uxbox.metrics :as mtx]))
|
||||||
|
|
||||||
|
(defn wrap-spec
|
||||||
|
[handler]
|
||||||
|
(let [mdata (meta handler)
|
||||||
|
spec (s/get-spec (:spec mdata))]
|
||||||
|
(if (nil? spec)
|
||||||
|
handler
|
||||||
|
(with-meta
|
||||||
|
(fn [params]
|
||||||
|
(let [result (us/conform spec params)]
|
||||||
|
(handler result)))
|
||||||
|
(assoc mdata ::wrap-spec true)))))
|
||||||
|
|
||||||
|
(defn wrap-error
|
||||||
|
[handler]
|
||||||
|
(let [mdata (meta handler)]
|
||||||
|
(with-meta
|
||||||
|
(fn [params]
|
||||||
|
(try
|
||||||
|
(handler params)
|
||||||
|
(catch Throwable error
|
||||||
|
(ex/raise :type :service-error
|
||||||
|
:name (:spec mdata)
|
||||||
|
:cause error))))
|
||||||
|
(assoc mdata ::wrap-error true))))
|
||||||
|
|
||||||
|
(defn- get-prefix
|
||||||
|
[nsname]
|
||||||
|
(let [[a b c] (str/split nsname ".")]
|
||||||
|
c))
|
||||||
|
|
||||||
|
(defn wrap-metrics
|
||||||
|
[handler]
|
||||||
|
(let [mdata (meta handler)
|
||||||
|
nsname (namespace (:spec mdata))
|
||||||
|
smname (name (:spec mdata))
|
||||||
|
prefix (get-prefix nsname)
|
||||||
|
|
||||||
|
sname (str prefix "/" smname)
|
||||||
|
|
||||||
|
props {:id (str/join "__" [prefix
|
||||||
|
(str/snake smname)
|
||||||
|
"response_time"])
|
||||||
|
:help (str "Service timing measures for: " sname ".")}]
|
||||||
|
(with-meta
|
||||||
|
(mtx/wrap-summary handler props)
|
||||||
|
(assoc mdata ::wrap-metrics true))))
|
||||||
|
|
||||||
|
(defn wrap
|
||||||
|
[handler]
|
||||||
|
(-> handler
|
||||||
|
(wrap-spec)
|
||||||
|
(wrap-error)
|
||||||
|
(wrap-metrics)))
|
|
@ -5,16 +5,16 @@
|
||||||
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
;; defined by the Mozilla Public License, v. 2.0.
|
;; defined by the Mozilla Public License, v. 2.0.
|
||||||
;;
|
;;
|
||||||
;; Copyright (c) 2019-2020 Andrey Antukh <niwi@niwi.nz>
|
;; Copyright (c) 2020 UXBOX Labs SL
|
||||||
|
|
||||||
(ns uxbox.services.mutations
|
(ns uxbox.services.mutations
|
||||||
(:require
|
(:require
|
||||||
|
[uxbox.services.middleware :as middleware]
|
||||||
[uxbox.util.dispatcher :as uds]))
|
[uxbox.util.dispatcher :as uds]))
|
||||||
|
|
||||||
(uds/defservice handle
|
(uds/defservice handle
|
||||||
:dispatch-by ::type
|
:dispatch-by ::type
|
||||||
:wrap [uds/wrap-spec
|
:wrap middleware/wrap)
|
||||||
uds/wrap-error])
|
|
||||||
|
|
||||||
(defmacro defmutation
|
(defmacro defmutation
|
||||||
[key & rest]
|
[key & rest]
|
||||||
|
|
|
@ -13,8 +13,9 @@
|
||||||
[ring.adapter.jetty9 :as jetty]
|
[ring.adapter.jetty9 :as jetty]
|
||||||
[uxbox.common.exceptions :as ex]
|
[uxbox.common.exceptions :as ex]
|
||||||
[uxbox.common.uuid :as uuid]
|
[uxbox.common.uuid :as uuid]
|
||||||
[uxbox.redis :as redis]
|
|
||||||
[uxbox.db :as db]
|
[uxbox.db :as db]
|
||||||
|
[uxbox.redis :as redis]
|
||||||
|
[uxbox.metrics :as mtx]
|
||||||
[uxbox.util.time :as dt]
|
[uxbox.util.time :as dt]
|
||||||
[uxbox.util.transit :as t]))
|
[uxbox.util.transit :as t]))
|
||||||
|
|
||||||
|
@ -193,11 +194,20 @@
|
||||||
(jetty/send! conn (t/encode-str val))
|
(jetty/send! conn (t/encode-str val))
|
||||||
(recur)))))
|
(recur)))))
|
||||||
|
|
||||||
|
(defonce metrics-active-connections
|
||||||
|
(mtx/gauge {:id "notificatons__active_connections"
|
||||||
|
:help "Active connections to the notifications service."}))
|
||||||
|
|
||||||
|
(defonce metrics-message-counter
|
||||||
|
(mtx/counter {:id "notificatons__messages_counter"
|
||||||
|
:help "A total number of messages handled by the notifications service."}))
|
||||||
|
|
||||||
(defn websocket
|
(defn websocket
|
||||||
[{:keys [file-id] :as params}]
|
[{:keys [file-id] :as params}]
|
||||||
(let [in (a/chan 32)
|
(let [in (a/chan 32)
|
||||||
out (a/chan 32)]
|
out (a/chan 32)]
|
||||||
{:on-connect (fn [conn]
|
{:on-connect (fn [conn]
|
||||||
|
(metrics-active-connections :inc)
|
||||||
(let [xf (map t/decode-str)
|
(let [xf (map t/decode-str)
|
||||||
sub (redis/subscribe (str file-id) xf)
|
sub (redis/subscribe (str file-id) xf)
|
||||||
ws (WebSocket. conn in out sub nil params)]
|
ws (WebSocket. conn in out sub nil params)]
|
||||||
|
@ -207,21 +217,19 @@
|
||||||
(a/close! sub))))
|
(a/close! sub))))
|
||||||
|
|
||||||
:on-error (fn [conn e]
|
:on-error (fn [conn e]
|
||||||
;; (prn "websocket" :on-error e)
|
|
||||||
(a/close! out)
|
(a/close! out)
|
||||||
(a/close! in))
|
(a/close! in))
|
||||||
|
|
||||||
:on-close (fn [conn status-code reason]
|
:on-close (fn [conn status-code reason]
|
||||||
;; (prn "websocket" :on-close status-code reason)
|
(metrics-active-connections :dec)
|
||||||
(a/close! out)
|
(a/close! out)
|
||||||
(a/close! in))
|
(a/close! in))
|
||||||
|
|
||||||
:on-text (fn [ws message]
|
:on-text (fn [ws message]
|
||||||
|
(metrics-message-counter :inc)
|
||||||
(let [message (t/decode-str message)]
|
(let [message (t/decode-str message)]
|
||||||
;; (prn "websocket" :on-text message)
|
|
||||||
(a/>!! in message)))
|
(a/>!! in message)))
|
||||||
|
|
||||||
:on-bytes (fn [ws bytes offset len]
|
:on-bytes (constantly nil)}))
|
||||||
#_(prn "websocket" :on-bytes bytes))}))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,16 +2,19 @@
|
||||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
;; 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/.
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
;;
|
;;
|
||||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
|
;; defined by the Mozilla Public License, v. 2.0.
|
||||||
|
;;
|
||||||
|
;; Copyright (c) 2020 UXBOX Labs SL
|
||||||
|
|
||||||
(ns uxbox.services.queries
|
(ns uxbox.services.queries
|
||||||
(:require
|
(:require
|
||||||
|
[uxbox.services.middleware :as middleware]
|
||||||
[uxbox.util.dispatcher :as uds]))
|
[uxbox.util.dispatcher :as uds]))
|
||||||
|
|
||||||
(uds/defservice handle
|
(uds/defservice handle
|
||||||
:dispatch-by ::type
|
:dispatch-by ::type
|
||||||
:wrap [uds/wrap-spec
|
:wrap middleware/wrap)
|
||||||
uds/wrap-error])
|
|
||||||
|
|
||||||
(defmacro defquery
|
(defmacro defquery
|
||||||
[key & rest]
|
[key & rest]
|
||||||
|
|
|
@ -10,8 +10,6 @@
|
||||||
(ns uxbox.services.queries.icons
|
(ns uxbox.services.queries.icons
|
||||||
(:require
|
(:require
|
||||||
[clojure.spec.alpha :as s]
|
[clojure.spec.alpha :as s]
|
||||||
[promesa.core :as p]
|
|
||||||
[promesa.exec :as px]
|
|
||||||
[uxbox.common.exceptions :as ex]
|
[uxbox.common.exceptions :as ex]
|
||||||
[uxbox.common.spec :as us]
|
[uxbox.common.spec :as us]
|
||||||
[uxbox.common.uuid :as uuid]
|
[uxbox.common.uuid :as uuid]
|
||||||
|
|
|
@ -10,13 +10,12 @@
|
||||||
(ns uxbox.services.queries.images
|
(ns uxbox.services.queries.images
|
||||||
(:require
|
(:require
|
||||||
[clojure.spec.alpha :as s]
|
[clojure.spec.alpha :as s]
|
||||||
[promesa.core :as p]
|
|
||||||
[uxbox.common.exceptions :as ex]
|
[uxbox.common.exceptions :as ex]
|
||||||
[uxbox.common.spec :as us]
|
[uxbox.common.spec :as us]
|
||||||
[uxbox.db :as db]
|
[uxbox.db :as db]
|
||||||
[uxbox.images :as images]
|
[uxbox.images :as images]
|
||||||
[uxbox.services.queries.teams :as teams]
|
[uxbox.services.queries :as sq]
|
||||||
[uxbox.services.queries :as sq]))
|
[uxbox.services.queries.teams :as teams]))
|
||||||
|
|
||||||
(s/def ::id ::us/uuid)
|
(s/def ::id ::us/uuid)
|
||||||
(s/def ::name ::us/string)
|
(s/def ::name ::us/string)
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
[uxbox.common.spec :as us]
|
[uxbox.common.spec :as us]
|
||||||
[uxbox.config :as cfg]
|
[uxbox.config :as cfg]
|
||||||
[uxbox.db :as db]
|
[uxbox.db :as db]
|
||||||
|
[uxbox.metrics :as mtx]
|
||||||
[uxbox.tasks.sendmail]
|
[uxbox.tasks.sendmail]
|
||||||
[uxbox.tasks.gc]
|
[uxbox.tasks.gc]
|
||||||
[uxbox.tasks.remove-media]
|
[uxbox.tasks.remove-media]
|
||||||
|
@ -68,3 +69,8 @@
|
||||||
([conn opts]
|
([conn opts]
|
||||||
(s/assert ::impl/task-options opts)
|
(s/assert ::impl/task-options opts)
|
||||||
(impl/submit! conn opts)))
|
(impl/submit! conn opts)))
|
||||||
|
|
||||||
|
(mtx/instrument-with-counter!
|
||||||
|
{:var #'submit!
|
||||||
|
:id "tasks__submit_counter"
|
||||||
|
:help "Absolute task submit counter."})
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
[uxbox.common.exceptions :as ex]
|
[uxbox.common.exceptions :as ex]
|
||||||
[uxbox.common.spec :as us]
|
[uxbox.common.spec :as us]
|
||||||
[uxbox.db :as db]
|
[uxbox.db :as db]
|
||||||
[uxbox.media :as media]
|
[uxbox.metrics :as mtx]
|
||||||
[uxbox.util.storage :as ust]))
|
[uxbox.util.storage :as ust]))
|
||||||
|
|
||||||
(s/def ::type keyword?)
|
(s/def ::type keyword?)
|
||||||
|
@ -36,6 +36,11 @@
|
||||||
(db/with-atomic [conn db/pool]
|
(db/with-atomic [conn db/pool]
|
||||||
(handle-deletion conn props)))
|
(handle-deletion conn props)))
|
||||||
|
|
||||||
|
(mtx/instrument-with-summary!
|
||||||
|
{:var #'handler
|
||||||
|
:id "tasks__delete_object"
|
||||||
|
:help "Timing of remove-object task."})
|
||||||
|
|
||||||
(defmethod handle-deletion :image
|
(defmethod handle-deletion :image
|
||||||
[conn {:keys [id] :as props}]
|
[conn {:keys [id] :as props}]
|
||||||
(let [sql "delete from image where id=? and deleted_at is not null"]
|
(let [sql "delete from image where id=? and deleted_at is not null"]
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
[uxbox.common.exceptions :as ex]
|
[uxbox.common.exceptions :as ex]
|
||||||
[uxbox.common.spec :as us]
|
[uxbox.common.spec :as us]
|
||||||
[uxbox.db :as db]
|
[uxbox.db :as db]
|
||||||
[uxbox.media :as media]
|
[uxbox.metrics :as mtx]
|
||||||
[uxbox.util.storage :as ust]))
|
[uxbox.util.storage :as ust]))
|
||||||
|
|
||||||
(declare delete-profile-data)
|
(declare delete-profile-data)
|
||||||
|
@ -39,6 +39,11 @@
|
||||||
(log/warn "Profile " (:id profile)
|
(log/warn "Profile " (:id profile)
|
||||||
"does not match constraints for deletion")))))
|
"does not match constraints for deletion")))))
|
||||||
|
|
||||||
|
(mtx/instrument-with-summary!
|
||||||
|
{:var #'handler
|
||||||
|
:id "tasks__delete_profile"
|
||||||
|
:help "Timing of delete-profile task."})
|
||||||
|
|
||||||
(defn- delete-profile-data
|
(defn- delete-profile-data
|
||||||
[conn profile-id]
|
[conn profile-id]
|
||||||
(log/info "Proceding to delete all data related to profile" profile-id)
|
(log/info "Proceding to delete all data related to profile" profile-id)
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
[uxbox.common.exceptions :as ex]
|
[uxbox.common.exceptions :as ex]
|
||||||
[uxbox.common.spec :as us]
|
[uxbox.common.spec :as us]
|
||||||
[uxbox.media :as media]
|
[uxbox.media :as media]
|
||||||
|
[uxbox.metrics :as mtx]
|
||||||
[uxbox.util.storage :as ust]))
|
[uxbox.util.storage :as ust]))
|
||||||
|
|
||||||
(s/def ::path ::us/not-empty-string)
|
(s/def ::path ::us/not-empty-string)
|
||||||
|
@ -28,3 +29,7 @@
|
||||||
(ust/delete! media/media-storage (:path props))
|
(ust/delete! media/media-storage (:path props))
|
||||||
(log/debug "Media " (:path props) " removed.")))
|
(log/debug "Media " (:path props) " removed.")))
|
||||||
|
|
||||||
|
(mtx/instrument-with-summary!
|
||||||
|
{:var #'handler
|
||||||
|
:id "tasks__remove_media"
|
||||||
|
:help "Timing of remove-media task."})
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
[uxbox.common.data :as d]
|
[uxbox.common.data :as d]
|
||||||
[uxbox.common.exceptions :as ex]
|
[uxbox.common.exceptions :as ex]
|
||||||
[uxbox.config :as cfg]
|
[uxbox.config :as cfg]
|
||||||
|
[uxbox.metrics :as mtx]
|
||||||
[uxbox.util.http :as http]))
|
[uxbox.util.http :as http]))
|
||||||
|
|
||||||
(defmulti sendmail (fn [config email] (:sendmail-backend config)))
|
(defmulti sendmail (fn [config email] (:sendmail-backend config)))
|
||||||
|
@ -94,3 +95,7 @@
|
||||||
[{:keys [props] :as task}]
|
[{:keys [props] :as task}]
|
||||||
(sendmail cfg/config props))
|
(sendmail cfg/config props))
|
||||||
|
|
||||||
|
(mtx/instrument-with-summary!
|
||||||
|
{:var #'handler
|
||||||
|
:id "tasks__sendmail"
|
||||||
|
:help "Timing of sendmail task."})
|
||||||
|
|
|
@ -20,22 +20,18 @@
|
||||||
(definterface IDispatcher
|
(definterface IDispatcher
|
||||||
(^void add [key f]))
|
(^void add [key f]))
|
||||||
|
|
||||||
(defn- wrap-handler
|
(deftype Dispatcher [reg attr wrap]
|
||||||
[items handler]
|
|
||||||
(reduce #(%2 %1) handler items))
|
|
||||||
|
|
||||||
(deftype Dispatcher [reg attr wrap-fns]
|
|
||||||
IDispatcher
|
IDispatcher
|
||||||
(add [this key f]
|
(add [this key f]
|
||||||
(let [f (wrap-handler wrap-fns f)]
|
(.put ^Map reg key (wrap f))
|
||||||
(.put ^Map reg key f)
|
this)
|
||||||
this))
|
|
||||||
|
|
||||||
clojure.lang.IDeref
|
clojure.lang.IDeref
|
||||||
(deref [_]
|
(deref [_]
|
||||||
{:registry reg
|
{:registry reg
|
||||||
:attr attr
|
:attr attr
|
||||||
:wrap-fns wrap-fns})
|
:wrap wrap})
|
||||||
|
|
||||||
clojure.lang.IFn
|
clojure.lang.IFn
|
||||||
(invoke [_ params]
|
(invoke [_ params]
|
||||||
|
@ -100,36 +96,3 @@
|
||||||
`(do
|
`(do
|
||||||
(s/assert dispatcher? ~sym)
|
(s/assert dispatcher? ~sym)
|
||||||
(add-method ~sym ~key ~f ~meta))))
|
(add-method ~sym ~key ~f ~meta))))
|
||||||
|
|
||||||
(defn wrap-spec
|
|
||||||
[handler]
|
|
||||||
(let [mdata (meta handler)
|
|
||||||
spec (s/get-spec (:spec mdata))]
|
|
||||||
(if (nil? spec)
|
|
||||||
handler
|
|
||||||
(with-meta
|
|
||||||
(fn [params]
|
|
||||||
(let [result (s/conform spec params)]
|
|
||||||
(if (not= result ::s/invalid)
|
|
||||||
(handler result)
|
|
||||||
(let [data (s/explain-data spec params)]
|
|
||||||
(ex/raise :type :validation
|
|
||||||
:code :spec-validation
|
|
||||||
:explain (with-out-str
|
|
||||||
(expound/printer data))
|
|
||||||
:data (::s/problems data))))))
|
|
||||||
(assoc mdata ::wrap-spec true)))))
|
|
||||||
|
|
||||||
(defn wrap-error
|
|
||||||
[handler]
|
|
||||||
(let [mdata (meta handler)]
|
|
||||||
(with-meta
|
|
||||||
(fn [params]
|
|
||||||
(try
|
|
||||||
(handler params)
|
|
||||||
(catch Throwable error
|
|
||||||
(ex/raise :type :service-error
|
|
||||||
:name (:spec mdata)
|
|
||||||
:cause error))))
|
|
||||||
(assoc mdata ::wrap-error true))))
|
|
||||||
|
|
||||||
|
|
|
@ -19,11 +19,9 @@
|
||||||
[clojure.repl :refer :all]
|
[clojure.repl :refer :all]
|
||||||
[criterium.core :refer [quick-bench bench with-progress-reporting]]
|
[criterium.core :refer [quick-bench bench with-progress-reporting]]
|
||||||
[clj-kondo.core :as kondo]
|
[clj-kondo.core :as kondo]
|
||||||
[promesa.core :as p]
|
|
||||||
[promesa.exec :as px]
|
|
||||||
[uxbox.migrations]
|
[uxbox.migrations]
|
||||||
[uxbox.db :as db]
|
[uxbox.db :as db]
|
||||||
;; [uxbox.redis :as rd]
|
[uxbox.metrics :as mtx]
|
||||||
[uxbox.util.storage :as st]
|
[uxbox.util.storage :as st]
|
||||||
[uxbox.util.time :as tm]
|
[uxbox.util.time :as tm]
|
||||||
[uxbox.util.blob :as blob]
|
[uxbox.util.blob :as blob]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue