Skip to content

Commit e548da1

Browse files
committed
handle multiple reloads
Signed-off-by: Tim Meusel <tim@bastelfreak.de>
1 parent 7ab2994 commit e548da1

3 files changed

Lines changed: 59 additions & 3 deletions

File tree

project.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@
9595
[org.openvoxproject/i18n ~i18n-version]]
9696

9797
:eastwood {:ignored-faults {:reflection {puppetlabs.trapperkeeper.logging [{:line 92}]
98-
puppetlabs.trapperkeeper.internal [{:line 194}]
98+
puppetlabs.trapperkeeper.internal [{:line 220}]
9999
puppetlabs.trapperkeeper.testutils.logging true
100100
puppetlabs.trapperkeeper.testutils.logging-test true
101101
puppetlabs.trapperkeeper.services.nrepl.nrepl-service-test true

src/puppetlabs/trapperkeeper/internal.clj

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,32 @@
102102

103103
(def max-pending-lifecycle-events 5)
104104

105+
;; Debounce at the signal handler boundary to avoid back-to-back
106+
;; restart cycles that can interrupt in-flight agent requests.
107+
(def min-sighup-restart-interval-ms 500)
108+
(def last-sighup-restart-ms (atom nil))
109+
110+
(defn now-ms []
111+
(System/currentTimeMillis))
112+
113+
(defn should-handle-sighup-restart?
114+
[]
115+
(let [current-ms (now-ms)
116+
accepted? (atom false)]
117+
(swap! last-sighup-restart-ms
118+
(fn [last-ms]
119+
(if (and (some? last-ms)
120+
(< (- current-ms last-ms) min-sighup-restart-interval-ms))
121+
last-ms
122+
(do
123+
(reset! accepted? true)
124+
current-ms))))
125+
@accepted?))
126+
127+
(defn app-log-id
128+
[app]
129+
(str "app=0x" (Integer/toHexString (System/identityHashCode app))))
130+
105131
(defn service-graph?
106132
"Predicate that tests whether or not the argument is a valid trapperkeeper
107133
service graph."
@@ -368,6 +394,7 @@
368394
[apps]
369395
(log/info (i18n/trs "SIGHUP handler restarting TK apps."))
370396
(doseq [app apps]
397+
(log/info (i18n/trs "Queueing SIGHUP restart for TK {0}" (app-log-id app)))
371398
(let [{:keys [lifecycle-channel]} @(a/app-context app)
372399
restart-fn #(a/restart app)]
373400
(when-not (async/offer! lifecycle-channel
@@ -376,6 +403,14 @@
376403
(log/warn (i18n/trs "Ignoring new SIGHUP restart requests; too many requests queued ({0})"
377404
max-pending-lifecycle-events))))))
378405

406+
(defn maybe-restart-tk-apps
407+
"Restart all TK apps unless this SIGHUP request is a rapid duplicate."
408+
[apps]
409+
(if (should-handle-sighup-restart?)
410+
(restart-tk-apps apps)
411+
(log/warn (i18n/trs "Ignoring duplicate SIGHUP restart request received within {0} ms"
412+
min-sighup-restart-interval-ms))))
413+
379414
(defn register-sighup-handler
380415
"Register a handler for SIGHUP that restarts all trapperkeeper apps. The
381416
default handler terminates the process, so we always overwrite that. This
@@ -384,7 +419,7 @@
384419
(register-sighup-handler @tk-apps))
385420
([apps]
386421
(log/debug (i18n/trs "Registering SIGHUP handler for restarting TK apps"))
387-
(reset! (beckon/signal-atom "HUP") #{(partial restart-tk-apps apps)})))
422+
(reset! (beckon/signal-atom "HUP") #{(partial maybe-restart-tk-apps apps)})))
388423

389424
;;;; Application Shutdown Support
390425
;;;;
@@ -767,6 +802,7 @@
767802
this)))
768803
(a/restart [this]
769804
(try
805+
(log/info (i18n/trs "Starting restart for TK {0}" (app-log-id this)))
770806
(notice-service-reloading)
771807
(run-lifecycle-fns app-context s/stop "stop" (reverse ordered-services))
772808
(doseq [svc-id (keys services-by-id)] (swap! app-context assoc-in [:service-contexts svc-id] {}))
@@ -775,6 +811,7 @@
775811
(run-lifecycle-fns app-context s/start "start" ordered-services)
776812
(enable-ready-notifications-for-app! services-by-id)
777813
(notice-service-ready-if-uncoordinated! services-by-id)
814+
(log/info (i18n/trs "Finished restart for TK {0}" (app-log-id this)))
778815
(inc-restart-counter! this)
779816
this
780817
(catch Throwable t

test/puppetlabs/trapperkeeper/internal_test.clj

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,4 +108,23 @@
108108
(tk-app/stop app)
109109
;; and make sure that we got one last :stop
110110
(is (= (conj expected-lifecycle-events :stop)
111-
@lifecycle-events)))))
111+
@lifecycle-events)))))
112+
113+
(deftest test-sighup-restart-debounce
114+
(let [restart-calls (atom 0)]
115+
(with-redefs [internal/now-ms (let [times (atom [1000 1100 2000])]
116+
(fn []
117+
(let [t (first @times)]
118+
(swap! times rest)
119+
t)))
120+
internal/restart-tk-apps (fn [_apps]
121+
(swap! restart-calls inc))
122+
internal/last-sighup-restart-ms (atom nil)]
123+
(internal/maybe-restart-tk-apps [:app])
124+
(logging/with-test-logging
125+
(internal/maybe-restart-tk-apps [:app])
126+
(is (logged? "Ignoring duplicate SIGHUP restart request received within 500 ms"
127+
:warn)
128+
"Missing expected warning log for duplicate SIGHUP"))
129+
(internal/maybe-restart-tk-apps [:app])
130+
(is (= 2 @restart-calls)))))

0 commit comments

Comments
 (0)