-
Notifications
You must be signed in to change notification settings - Fork 2
/
checks.clj
154 lines (133 loc) · 5.42 KB
/
checks.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
(ns salutem.core.checks
"Provides constructors, predicates and evaluation functions for checks."
(:require
[clojure.core.async :as async]
[tick.alpha.api :as t]
[cartus.core :as log]
[cartus.null :as cartus-null]
[salutem.core.results :as results]))
(defn- check
([check-name check-fn]
(check check-name check-fn {}))
([check-name check-fn
{:keys [timeout]
:or {timeout (t/new-duration 10 :seconds)}
:as opts}]
(merge
opts
{:name check-name
:check-fn check-fn
:timeout timeout})))
(defn background-check
"Constructs a background check with the provided name and check function.
A background check is one that is evaluated periodically with the result
cached until the next evaluation, which will occur once the time-to-live
(TTL) of the check has passed.
Background checks are useful for external dependencies where it is
important not to perform the check too frequently and where the health
status only needs to be accurate to within the TTL.
Takes the following parameters:
- `check-name`: a keyword representing the name of the check
- `check-fn`: an arity-2 function, with with the first argument being a
context map as provided during evaluation or at maintenance pipeline
construction and the second argument being a callback function which
should be called with the result fo the check to signal the check is
complete; note, check functions _must_ be non-blocking.
- `opts`: an optional map of additional options for the check, containing:
- `:ttl`: a [[duration]] representing the TTL for a result of this check,
defaulting to 10 seconds
- `:timeout`: a [[duration]] representing the amount of time to wait for
the check to complete before considering it failed, defaulting to
10 seconds"
([check-name check-fn]
(background-check check-name check-fn {}))
([check-name check-fn opts]
(check check-name check-fn
(merge
{:ttl (t/new-duration 10 :seconds)}
opts
{:type :background}))))
(defn realtime-check
"Constructs a realtime check with the provided name and check function.
A realtime check is one that is re-evaluated whenever the check is resolved,
with no caching of results taking place.
Realtime checks are useful when the accuracy of the check needs to be very
accurate or where the check itself is inexpensive.
Takes the following parameters:
- `check-name`: a keyword representing the name of the check
- `check-fn`: an arity-2 function, with with the first argument being a
context map as provided during evaluation or at maintenance pipeline
construction and the second argument being a callback function which
should be called with the result fo the check to signal the check is
complete; note, check functions _must_ be non-blocking.
- `opts`: an optional map of additional options for the check, containing:
- `:timeout`: a [[duration]] representing the amount of time to wait for
the check to complete before considering it failed, defaulting to
10 seconds"
([check-name check-fn]
(realtime-check check-name check-fn {}))
([check-name check-fn opts]
(check check-name check-fn
(merge
opts
{:type :realtime}))))
(defn background?
"Returns `true` if the provided check is a background check, `false`
otherwise."
[check]
(= (:type check) :background))
(defn realtime?
"Returns `true` if the provided check is a realtime check, `false`
otherwise."
[check]
(= (:type check) :realtime))
(defn attempt
([check context]
(let [logger (or (:logger context) (cartus-null/logger))
dependencies {:logger logger}]
(attempt dependencies nil check context (async/chan 1))))
([dependencies trigger-id check context result-channel]
(let [logger (:logger dependencies)
check-name (:name check)]
(async/go
(let [{:keys [check-fn timeout]} check
callback-channel (async/chan)]
(log/info logger ::attempt.starting
{:trigger-id trigger-id
:check-name check-name})
(check-fn context
(fn [result]
(async/put! callback-channel result)))
(async/alt!
callback-channel
([result]
(do
(log/info logger ::attempt.completed
{:trigger-id trigger-id
:check-name check-name
:result result})
(async/>! result-channel
{:trigger-id trigger-id
:check check
:result result})))
(async/timeout (t/millis timeout))
(do
(log/info logger ::attempt.timed-out
{:trigger-id trigger-id
:check-name check-name})
(async/>! result-channel
{:trigger-id trigger-id
:check check
:result (results/unhealthy)}))
:priority true)
(async/close! callback-channel))))
result-channel))
(defn evaluate
"Evaluates the provided check synchronously, returning the result of the
evaluation.
Optionally takes a context map containing arbitrary context required
by the check in order to run and passed to the check function as the first
argument."
([check] (evaluate check {}))
([check context]
(:result (async/<!! (attempt check context)))))