-
Notifications
You must be signed in to change notification settings - Fork 2
/
registry.clj
145 lines (124 loc) · 5.32 KB
/
registry.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
(ns salutem.core.registry
"Provides constructors, query functions and resolution functions for
registries."
(:require
[salutem.core.checks :as checks]
[salutem.core.results :as results]))
(defn empty-registry
"Constructs an empty registry which can be populated using [[with-check]] and
[[with-cached-result]]."
[]
{:salutem/checks {}
:salutem/cached-results {}})
(defn with-check
"Adds the check to the registry, returning a new registry."
[registry check]
(update-in registry [:salutem/checks] assoc (:salutem/name check) check))
(defn with-cached-result
"Adds the result for the check with the given name to the registry,
returning a new registry."
[registry check-name result]
(update-in registry [:salutem/cached-results] assoc check-name result))
(defn find-check
"Finds the check with the given name in the registry. Returns `nil` if no
check can be found."
[registry check-name]
(get-in registry [:salutem/checks check-name]))
(defn find-cached-result
"Finds the cached result for the check with the given name in the registry.
Returns `nil` if no result can be found or if the check does not exist."
[registry check-name]
(get-in registry [:salutem/cached-results check-name]))
(defn check-names
"Returns the set of check names present in the registry."
[registry]
(set (keys (:salutem/checks registry))))
(defn all-checks
"Returns the set of checks present in the registry."
[registry]
(set (vals (:salutem/checks registry))))
(defn outdated-checks
"Returns the set of checks that are currently outdated in the registry based
on the type of the check and the cached results available.
See [[salutem.results/outdated?]] for details on which it means for a check
to be outdated."
[registry]
(set
(filter
#(results/outdated? (find-cached-result registry (:salutem/name %)) %)
(all-checks registry))))
(defn- requires-re-evaluation? [check result]
(or (checks/realtime? check) (not result)))
(defn resolve-check
"Resolves a result for the check of the given name in the registry.
If the check is a background check and there is a cached result available,
it is returned. If no cached result is available, the check is evaluated in
order to obtain a result to return.
If the check is a realtime check, it is always evaluated in order to obtain
a result to return and caching is not used.
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.
By default, the check is resolved synchronously. If a callback function is
provided, the function starts resolution asynchronously, returns immediately
and invokes the callback function with the result once available."
([registry check-name]
(resolve-check registry check-name {}))
([registry check-name context]
(let [promise (promise)]
(resolve-check registry check-name context #(deliver promise %))
(deref promise)))
([registry check-name context callback-fn]
(let [check (find-check registry check-name)
result (find-cached-result registry check-name)]
(if (requires-re-evaluation? check result)
(checks/evaluate check context callback-fn)
(callback-fn result)))))
(defn resolve-checks
"Resolves all checks in the registry, returning a map of check names to
results.
Checks requiring re-evaluation are evaluated in parallel such that this
function should take about as long as the slowest check (assuming IO is the
dominant blocker).
Optionally takes a context map containing arbitrary context required by
checks in order to run and passed to the check functions as the first
argument.
By default, the checks are resolved synchronously. If a callback function is
provided, the function starts resolution asynchronously, returns immediately
and invokes the callback function with the results once available.
See [[resolve-check]] for details on how each check is resolved."
([registry]
(resolve-checks registry {}))
([registry context]
(let [promise (promise)]
(resolve-checks registry context #(deliver promise %))
(deref promise)))
([registry context callback-fn]
(let [{:keys [requiring-re-evaluation resolved-from-cache]}
(reduce
(fn [accumulator check-name]
(let [check (find-check registry check-name)
result (find-cached-result registry check-name)]
(apply update-in accumulator
(if (requires-re-evaluation? check result)
[[:requiring-re-evaluation] conj check]
[[:resolved-from-cache] assoc check-name result]))))
{:requiring-re-evaluation []
:resolved-from-cache {}}
(check-names registry))
re-evaluation-promises
(map
(fn [check]
(let [promise (promise)]
(checks/evaluate check context
(fn [result]
(deliver promise [(:salutem/name check) result])))
promise))
requiring-re-evaluation)]
(future
(let [resolved-through-re-evaluation
(into {} (map deref re-evaluation-promises))]
(callback-fn
(merge
resolved-from-cache
resolved-through-re-evaluation)))))))