-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathtry.clj
More file actions
81 lines (69 loc) · 3.24 KB
/
try.clj
File metadata and controls
81 lines (69 loc) · 3.24 KB
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
(ns flc-x.try
"This namespace will be moved to flc-x/try."
(:require [flc.process :as process :refer [process*]]
[flc.component :as component]
[flc.map-like :as m]
[flc.utils :refer :all]))
(defn success
"Constructor for successes."
[x]
{:success x})
(defn failure
"Constructor for failures."
[x]
{:failure x})
(defn success? [x]
(and (map? x)
(contains? x :success)))
(defn failure? [x]
(and (map? x)
(contains? x :failure)))
(defmacro try-then [body then & more]
`(let [foo# (try
{::value ~body}
~@more)]
(if (and (map? foo#)
(contains? foo# ::value))
(~then (::value foo#))
foo#)))
(defn ->try [program start-failed dependencies-failed]
(fn [& args]
(if-let [failures (seq (filter failure? args))]
(process* (failure (dependencies-failed {:args args :failures failures})))
(try-then (apply program (map :success args))
#(process/update-state % success)
(catch Exception e
(process* (failure (start-failed {:args args :exception e}))))))))
(defn map-components [f components]
(m/fmap-keyed (fn [name component]
(component/update-program component (=> f name (component/dependencies component))))
components))
(defn try-all [components]
(map-components (fn [program name deps]
(->try program
(fn [{:keys [args exception]}]
(ex-info "Component failed to start"
{:reason :start
:component name
:args args
:dependencies deps}
exception))
(fn [{:keys [args]}]
(ex-info "Component had dependencies that failed to start"
{:reason :dependency
:component name
:args args
:dependencies deps
:failures (for [[dep arg] (map vector deps args)
:when (failure? arg)]
dep)}))))
components))
(defn failing-dependencies
"Given the states from processes wrapped in `->try` (e.g. via `try-all`), return a failure graph represented by a map-like sequence, where the keys are the names of the failing components and the values are the names of the dependencies that caused it to fail (or empty if the component itself failed).
Returns nil if all components successfully started.
Note: only key/value items where the value tests true for `failure?` are considered. An unfortunate consequence of this is that if you pass the return value straight from `start!` and don't extract the states then nil will be returned and your services might have silently failed to start."
[states]
(seq (for [[name state] states
:when (failure? state)
:let [e (:failure state)]]
[name (:failures (ex-data e))])))