-
Notifications
You must be signed in to change notification settings - Fork 6
/
core.clj
156 lines (127 loc) · 5.62 KB
/
core.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
(ns resilience.core
(:import (io.github.resilience4j.circuitbreaker CircuitBreaker)
(io.github.resilience4j.retry Retry)
(io.github.resilience4j.bulkhead Bulkhead)
(io.github.resilience4j.ratelimiter RateLimiter)
(io.github.resilience4j.timelimiter TimeLimiter)
(java.util.function Supplier)))
(defmacro to-fn [& body]
`(fn [] (do ~@body)))
;; breaker
(defmacro execute-with-breaker
"Execute the following codes with the protection
of a circuit breaker given by `breaker` argument.
Please try to not put `clojure.core/recur` in `body`
otherwise you may get an infinite loop easily."
[breaker & body]
(let [breaker (vary-meta breaker assoc :tag `CircuitBreaker)
f (with-meta `(to-fn ~@body) {:tag `Callable})]
`(.executeCallable ~breaker ~f)))
(defn with-breaker
"Provide a function `f` which has no arguments and returns a function
which is decorated by a circuit breaker given by `breaker` argument.
Usually this is used within `resilience.core/execute` block."
[^CircuitBreaker breaker f]
(CircuitBreaker/decorateCallable breaker f))
;; retry
(defmacro execute-with-retry
"Execute the following codes with the protection
of a retry policy given by `retry` argument.
Please try to not put `clojure.core/recur` in `body`
otherwise you may get an infinite loop easily."
[retry & body]
(let [retry (vary-meta retry assoc :tag `Retry)
f (with-meta `(to-fn ~@body) {:tag `Callable})]
`(.executeCallable ~retry ~f)))
(defn with-retry
"Provide a function `f` which has no arguments and returns a function
which is decorated by a retry policy given by `retry` argument.
Usually this is used within `resilience.core/execute` block."
[^Retry retry f]
(Retry/decorateCallable retry f))
;; bulkhead
(defmacro execute-with-bulkhead
"Execute the following codes with the protection
of a bulkhead given by `bulkhead` argument.
Please try to not put `clojure.core/recur` in `body`
otherwise you may get an infinite loop easily."
[^Bulkhead bulkhead & body]
(let [bulkhead (vary-meta bulkhead assoc :tag `Bulkhead)
f (with-meta `(to-fn ~@body) {:tag `Callable})]
`(.executeCallable ~bulkhead ~f)))
(defn with-bulkhead
"Provide a function `f` which has no arguments and returns a function
which is decorated by a bulkhead given by `bulkhead` argument.
Usually this is used within `resilience.core/execute` block."
[^Bulkhead bulkhead f]
(Bulkhead/decorateCallable bulkhead f))
;; rate limiter
(defmacro execute-with-rate-limiter
[rate-limiter & body]
"Execute the following codes with the protection
of a rate limiter given by `rate-limiter` argument.
Please try to not put `clojure.core/recur` in `body`
otherwise you may get an infinite loop easily."
(let [ratelimiter (vary-meta rate-limiter assoc :tag `RateLimiter)
f (with-meta `(to-fn ~@body) {:tag `Callable})]
`(.executeCallable ~ratelimiter ~f)))
(defn with-rate-limiter
"Provide a function `f` which has no arguments and returns a function
which is decorated by a rate limiter given by `rate-limiter` argument.
Usually this is used within `resilience.core/execute` block."
[^RateLimiter rate-limiter f]
(RateLimiter/decorateCallable rate-limiter f))
;; time limiter
(defmacro execute-with-time-limiter
"Execute the following codes with the protection
of a time limiter given by `time-limiter` argument.
Please try to not put `clojure.core/recur` in `body`
otherwise you may get an infinite loop easily."
[^TimeLimiter timelimiter & body]
(let [timelimiter (vary-meta timelimiter assoc :tag `TimeLimiter)]
`(.executeFutureSupplier ~timelimiter (reify Supplier
(get [_] (do ~@body))))))
(defn with-time-limiter
"Provide a function `f` which has no arguments and returns a function
which is decorated by a time limiter given by `time-limiter` argument.
Usually this is used within `resilience.core/execute` block."
[^TimeLimiter time-limiter f]
(TimeLimiter/decorateFutureSupplier time-limiter
(reify Supplier
(get [_] (f)))))
;; gather together
(defmacro execute-callable* [f]
(let [f (vary-meta f assoc :tag `Callable)]
`(.call ^Callable ~f)))
(defmacro execute
[execute-body & args]
`(->> (to-fn ~execute-body)
~@args
execute-callable*))
(defmacro with-resilience-family [family-members & body]
(let [wrappers (map #(let [[k v] %]
(list (symbol (str "resilience.core/with-" (name k))) v))
(partition-all 2 family-members))]
`(->> (to-fn ~@body)
~@wrappers
execute-callable*)))
(defn- recover-from* [exceptions failover-fn wraped-fn]
(let [wraped-fn (vary-meta wraped-fn assoc :tag `Callable)
handler (gensym "handler-fn-")
catch-blocks (if (sequential? exceptions)
(let [ex-name-list (repeatedly (count exceptions)
(partial gensym "ex-"))]
(mapv #(list 'catch % %2
(list handler %2))
exceptions ex-name-list))
`((catch ~exceptions ex#
(~handler ex#))))]
`(let [~handler ~failover-fn]
(fn []
(try
(.call ~wraped-fn)
~@catch-blocks)))))
(defmacro recover-from [exceptions failover-fn wraped-fn]
(recover-from* exceptions failover-fn wraped-fn))
(defmacro recover [failover-fn wraped-fn]
(recover-from* 'Exception failover-fn wraped-fn))