Skip to content

Commit c3a2263

Browse files
committed
Remove async option
1 parent b50c1ad commit c3a2263

File tree

3 files changed

+12
-94
lines changed

3 files changed

+12
-94
lines changed

README.adoc

-27
Original file line numberDiff line numberDiff line change
@@ -366,9 +366,6 @@ The default config goes in the root of the project as `guardrails.edn`:
366366
:expound {:show-valid-values? true
367367
:print-specs? true}
368368
369-
;; Check specs in parallel (CLJ only)
370-
:async? true
371-
372369
;; GLOBALLY enable non-exhaustive checking (this is NOT recommended, you'd
373370
;; usually want to set it in a more granular fashion on the function or
374371
;; namespace level using metadata.)
@@ -501,30 +498,6 @@ The set of exclusions found in export files at load time is what `reset-exclusio
501498

502499
NOTE: As a library *author* these exclusions will end up applying to your code as well, since it is difficult to tell which export file belongs to which project on the classpath. Thus the beginning of your test namespaces (and possibly your non-published user ns) should all start with a call to `config/clear-exclusions!` if you want to include your implementation checks while running your tests and working on your library code.
503500

504-
=== Async Mode for Clojure (not useful in CLJS)
505-
506-
NOTE: This is an older attempt at helping performance, and it is moderately helpful when you want exhaustive checking in Clojure. In general, you should instead prefer the use of exclusions or the max checks per second setting at the namespace, function, or project level.
507-
508-
Guardrails has an asynchronous checking mode for Clojure.
509-
510-
When this mode is enabled it pushes spec checking into a queue with a dropping buffer (size 10,000), which is processed by another thread. The overhead for the `put` is just a few microseconds. This allows an alternate thread to run the checks, and as long as you don't have large sustained computations this can give you nearly full-production performance of your code, while an alternate core in your computer handles the checks.
511-
512-
Benefits:
513-
514-
* Much faster dev performance (Clojure only. The option does nothing in CLJS).
515-
* High performance algorithms can use guardrails with a tolerable cost.
516-
517-
Costs:
518-
519-
* Checking results are queued. If a lot of slow checks get in the queue you might have to wait some time before you see the problems. This could cause confusion (you might be running your next expression in the REPL and see an error from the prior one).
520-
* Not all checks will run in a CPU-intensive task that queues checks rapidly.
521-
* Async mode is incompatible with the `:throw? true` option.
522-
523-
To enable the async mode, just add `:async? true` in your `guardrails.edn` file.
524-
525-
This mode does not benefit clojurescript because there *is no* alternate thread to push the checks to.
526-
527-
528501
== Why?
529502

530503
Clojure spec's instrument (and Orchestra's outstrument) have a number of disadvantages when trying to use them for

src/main/com/fulcrologic/guardrails/config.cljc

+1-9
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,6 @@
108108
(defn mode [config]
109109
(get config :mode :runtime))
110110

111-
(defn async? [config]
112-
(get config :async? false))
113-
114111
(def default-config
115112
{;; Generates standard `defn` function definitions
116113
;; by default. If you require composability with other
@@ -151,15 +148,10 @@
151148
cljs-compiler-config
152149
(System/getProperty "guardrails.enabled"))
153150
:cljs false)
154-
(let [{:keys [async? throw?] :as result} (merge {} (read-config-file) cljs-compiler-config)
155-
result (if (and async? throw?)
156-
(dissoc result :async?)
157-
result)]
151+
(let [result (merge {} (read-config-file) cljs-compiler-config)]
158152
(when-not @warned?
159153
(reset! warned? true)
160154
(utils/report-info "GUARDRAILS IS ENABLED. RUNTIME PERFORMANCE WILL BE AFFECTED.")
161-
(when (and async? throw?)
162-
(utils/report-problem "INCOMPATIBLE MODES: :throw? and :async? cannot both be true. Disabling async."))
163155
(utils/report-info (str "Mode: " (mode result) (when (= :runtime (mode result))
164156
(str " config: " result))))
165157
(utils/report-info (str "Guardrails was enabled because "

src/main/com/fulcrologic/guardrails/core.cljc

+11-58
Original file line numberDiff line numberDiff line change
@@ -58,23 +58,6 @@
5858

5959
(defn- get-global-context [] (first @!global-context))
6060

61-
(defonce pending-checks
62-
#?(:clj (java.util.concurrent.ArrayBlockingQueue. 10000)
63-
:cljs nil))
64-
65-
66-
#?(:clj
67-
(defonce check-thread
68-
(doto (Thread. ^Runnable
69-
(fn []
70-
(try
71-
((.take ^java.util.concurrent.BlockingQueue pending-checks))
72-
(catch #?(:clj Exception :cljs :default) _))
73-
(recur))
74-
"Guardrails Async")
75-
(.setDaemon true)
76-
(.start))))
77-
7861
;; runtime checking (both clj and cljs
7962
(defn- output-fn [data]
8063
(let [{:keys [level ?err msg_ ?ns-str ?file hostname_
@@ -113,7 +96,7 @@
11396
((exp/custom-printer expound-options) explain-data))))
11497

11598
(defn run-check [{:guardrails/keys [malli? compact? use-stderr? validate-fn explain-fn fqnm humanize-fn]
116-
:keys [tap>? args? vararg? humanize-opts callsite throw? fn-name]
99+
:keys [tap>? args? vararg? humanize-opts throw? fn-name]
117100
:as options}
118101
spec
119102
value]
@@ -138,7 +121,7 @@
138121
:guardrails/compact? compact?
139122
:guardrails/fqnm fqnm
140123
:guardrails/args? args?))
141-
e (or callsite (ex-info "" {}))
124+
e (ex-info "" {})
142125
description (utils/problem-description
143126
(str
144127
"\nGuardrails:\n"
@@ -720,10 +703,6 @@
720703
:sym `(s/fdef ~fn-name ~@(generate-external-fspec bs false))
721704
:key `(s/def ~fn-name (s/fspec ~@(generate-external-fspec bs false))))))))
722705

723-
(defn callsite-exception []
724-
#?(:cljs (js/Error. "")
725-
:clj (AssertionError. "")))
726-
727706
;; The now-nano was taken from taoensso/encore. I didn't want to include that entire dep just for this one
728707
;; function. The are covered by Eclipse Public License - v 1.0. See https://github.com/taoensso/encore
729708
#?(:cljs (def ^:no-doc js-?window (when (exists? js/window) js/window))) ; Present iff in browser
@@ -776,8 +755,7 @@
776755
{max-checks-per-second :guardrails/mcps
777756
:guardrails/keys [use-stderr? compact? trace? stack-trace]
778757
:keys [throw? tap>? disable-exclusions?]} :config} cfg
779-
{:guardrails/keys [validate-fn explain-fn humanize-fn malli?]
780-
:keys [async-checks?]} env
758+
{:guardrails/keys [validate-fn explain-fn humanize-fn malli?]} env
781759
cljs? (cljs-env? env)
782760

783761
{:com.fulcrologic.guardrails.core/keys
@@ -819,39 +797,15 @@
819797
exclusion-coord (if disable-exclusions?
820798
[:undefined/function :undefined]
821799
[(keyword nspc (name fn-name)) (keyword nspc)])
822-
args-check (if #?(:clj async-checks? :cljs false)
823-
`(when-not (gr.cfg/-excluded? ~(first exclusion-coord) ~(second exclusion-coord))
824-
(let [e# (callsite-exception)]
825-
(.offer
826-
^java.util.concurrent.BlockingQueue pending-checks
827-
(fn []
828-
(when ~argspec
829-
(run-check (assoc
830-
~(assoc opts :args? true)
831-
:callsite e#)
832-
~argspec
833-
~arg-syms))))))
834-
`(when ~argspec
835-
(when-not (gr.cfg/-excluded? ~(first exclusion-coord) ~(second exclusion-coord))
836-
(run-check ~(assoc opts :args? true) ~argspec ~arg-syms))))
800+
args-check `(when ~argspec
801+
(when-not (gr.cfg/-excluded? ~(first exclusion-coord) ~(second exclusion-coord))
802+
(run-check ~(assoc opts :args? true) ~argspec ~arg-syms)))
837803
retspec (gensym "retspec")
838804
ret (gensym "ret")
839805
add-throttling? (number? max-checks-per-second)
840-
ret-check (if #?(:clj async-checks? :cljs false)
841-
`(when-not (gr.cfg/-excluded? ~(first exclusion-coord) ~(second exclusion-coord))
842-
(let [e# (callsite-exception)]
843-
(.offer
844-
^java.util.concurrent.BlockingQueue pending-checks
845-
(fn []
846-
(when ~retspec
847-
(run-check (assoc
848-
~(assoc opts :args? false)
849-
:callsite e#)
850-
~retspec
851-
~ret))))))
852-
`(when (and ~retspec
853-
(not (gr.cfg/-excluded? ~(first exclusion-coord) ~(second exclusion-coord))))
854-
(run-check ~(assoc opts :args? false) ~retspec ~ret)))
806+
ret-check `(when (and ~retspec
807+
(not (gr.cfg/-excluded? ~(first exclusion-coord) ~(second exclusion-coord))))
808+
(run-check ~(assoc opts :args? false) ~retspec ~ret))
855809
real-function `(fn ~'guardrails-wrapper ~raw-arg-vec ~@body-forms)
856810
f (gensym "f")
857811
call (if (boolean conformed-var-arg)
@@ -946,15 +900,14 @@
946900
#?(:clj
947901
(defn >defn* [env form body {:keys [private? guardrails/malli?] :as opts}]
948902
(let [cfg (gr.cfg/get-env-config)
949-
mode (gr.cfg/mode cfg)
950-
async? (gr.cfg/async? cfg)]
903+
mode (gr.cfg/mode cfg)]
951904
(cond
952905
(not cfg) (clean-defn 'defn body)
953906
(#{:copilot :pro} mode) `(do
954907
(defn ~@body)
955908
~(gr.pro/>defn-impl env body opts))
956909
(#{:runtime :all} mode)
957-
(cond-> (remove nil? (generate-defn body private? (assoc env :form form :async-checks? async? :guardrails/malli? malli?)))
910+
(cond-> (remove nil? (generate-defn body private? (assoc env :form form :guardrails/malli? malli?)))
958911
(cljs-env? env) clj->cljs
959912
(= :all mode) (-> vec (conj (gr.pro/>defn-impl env body opts)) seq))))))
960913

0 commit comments

Comments
 (0)