Skip to content

Commit 2077c14

Browse files
committed
Memoize coercers for better performance
1 parent ebd3d8e commit 2077c14

2 files changed

Lines changed: 22 additions & 13 deletions

File tree

src/compojure/api/meta.clj

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@
77
[plumbing.core :refer :all]
88
[plumbing.fnk.impl :as fnk-impl]
99
[ring.swagger.common :refer :all]
10-
[ring.swagger.schema :as schema]
1110
[ring.swagger.json-schema :as js]
1211
[ring.util.http-response :refer [internal-server-error]]
1312
[slingshot.slingshot :refer [throw+]]
1413
[schema.core :as s]
14+
[schema.coerce :as sc]
15+
[schema.utils :as su]
1516
[schema-tools.core :as st]))
1617

1718
;;
@@ -23,7 +24,7 @@
2324
'+compojure-api-request+)
2425

2526
(def +compojure-api-meta+
26-
"lexically bound meta-data for handlers. EXPERIMENTAL."
27+
"lexically bound meta-data for handlers."
2728
'+compojure-api-meta+)
2829

2930
(defmacro meta-container [meta & form]
@@ -46,6 +47,8 @@
4647
;; Schema
4748
;;
4849

50+
(def memoized-coercer (memoize sc/coercer))
51+
4952
(defn strict [schema]
5053
(dissoc schema 'schema.core/Keyword))
5154

@@ -59,8 +62,9 @@
5962
(if-let [{:keys [status] :as response} (handler request)]
6063
(if-let [schema (:schema (responses status))]
6164
(if-let [matcher (:response (mw/get-coercion-matcher-provider request))]
62-
(let [body (schema/coerce schema (:body response) matcher)]
63-
(if (schema/error? body)
65+
(let [coerce (memoized-coercer (value-of schema) matcher)
66+
body (coerce (:body response))]
67+
(if (su/error? body)
6468
(throw+ (assoc body :type ::ex/response-validation))
6569
(assoc response
6670
::serializable? true
@@ -75,8 +79,9 @@
7579
(assert (not (#{:query :json} type)) (str type " is DEPRECATED since 0.22.0. Use :body or :string instead."))
7680
`(let [value# (keywordize-keys (~key ~+compojure-api-request+))]
7781
(if-let [matcher# (~type (mw/get-coercion-matcher-provider ~+compojure-api-request+))]
78-
(let [result# (schema/coerce ~schema value# matcher#)]
79-
(if (schema/error? result#)
82+
(let [coerce# (memoized-coercer ~schema matcher#)
83+
result# (coerce# value#)]
84+
(if (su/error? result#)
8085
(throw+ (assoc result# :type ::ex/request-validation))
8186
result#))
8287
value#)))

test/compojure/api/perf_test.clj

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@
22
(:require [compojure.api.sweet :refer :all]
33
[compojure.api.test-utils :refer :all]
44
[criterium.core :as cc]
5-
[midje.sweet :refer :all]
65
[ring.util.http-response :refer :all]
76
[schema.core :as s]))
87

8+
;;
9+
;; start repl with `lein perf repl`. note, all numbers are from Tommi's
10+
;; laptop.
11+
;;
12+
913
(defn title [s]
1014
(println
1115
(str "\n\u001B[35m"
@@ -35,7 +39,7 @@
3539
(assert (= {:result 30} (second (call))))
3640
(cc/quick-bench (call)))
3741

38-
; 32µs
42+
; 32µs => 30µs (-6%)
3943

4044
(let [app (api
4145
(POST* "/plus" []
@@ -50,7 +54,7 @@
5054
(assert (= {:result 30} (second (call))))
5155
(cc/quick-bench (call)))
5256

53-
;; 104µs
57+
;; 104µs => 73µs (-30%)
5458

5559
(let [app (api
5660
(context* "/a" []
@@ -68,7 +72,7 @@
6872
(assert (= {:result 30} (second (call))))
6973
(cc/quick-bench (call)))
7074

71-
;; 113µs
75+
;; 113µs => 80µs (-30%)
7276

7377
(let [app (api
7478
(POST* "/echo" []
@@ -93,9 +97,9 @@
9397
(s/check Order (second (call)))
9498
(cc/quick-bench (call)))
9599

96-
;; 343µs
100+
;; 343µs => 175µs (-49%)
97101

98102
)
99103

100-
101-
(c-api-bench)
104+
(comment
105+
(c-api-bench))

0 commit comments

Comments
 (0)