Skip to content

Commit bcfdd08

Browse files
committed
handle multiple reloads
Signed-off-by: Tim Meusel <tim@bastelfreak.de>
1 parent 9b75d42 commit bcfdd08

3 files changed

Lines changed: 60 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 221}]
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: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,33 @@
102102

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

105+
;; Some init systems can deliver duplicate HUP events for a single reload
106+
;; request. Debounce at the signal handler boundary to avoid back-to-back
107+
;; restart cycles that can interrupt in-flight agent requests.
108+
(def min-sighup-restart-interval-ms 500)
109+
(def last-sighup-restart-ms (atom nil))
110+
111+
(defn now-ms []
112+
(System/currentTimeMillis))
113+
114+
(defn should-handle-sighup-restart?
115+
[]
116+
(let [current-ms (now-ms)
117+
accepted? (atom false)]
118+
(swap! last-sighup-restart-ms
119+
(fn [last-ms]
120+
(if (and (some? last-ms)
121+
(< (- current-ms last-ms) min-sighup-restart-interval-ms))
122+
last-ms
123+
(do
124+
(reset! accepted? true)
125+
current-ms))))
126+
@accepted?))
127+
128+
(defn app-log-id
129+
[app]
130+
(str "app=0x" (Integer/toHexString (System/identityHashCode app))))
131+
105132
(defn service-graph?
106133
"Predicate that tests whether or not the argument is a valid trapperkeeper
107134
service graph."
@@ -368,6 +395,7 @@
368395
[apps]
369396
(log/info (i18n/trs "SIGHUP handler restarting TK apps."))
370397
(doseq [app apps]
398+
(log/info (i18n/trs "Queueing SIGHUP restart for TK {0}" (app-log-id app)))
371399
(let [{:keys [lifecycle-channel]} @(a/app-context app)
372400
restart-fn #(a/restart app)]
373401
(when-not (async/offer! lifecycle-channel
@@ -376,6 +404,14 @@
376404
(log/warn (i18n/trs "Ignoring new SIGHUP restart requests; too many requests queued ({0})"
377405
max-pending-lifecycle-events))))))
378406

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

389425
;;;; Application Shutdown Support
390426
;;;;
@@ -767,6 +803,7 @@
767803
this)))
768804
(a/restart [this]
769805
(try
806+
(log/info (i18n/trs "Starting restart for TK {0}" (app-log-id this)))
770807
(notice-service-reloading)
771808
(run-lifecycle-fns app-context s/stop "stop" (reverse ordered-services))
772809
(doseq [svc-id (keys services-by-id)] (swap! app-context assoc-in [:service-contexts svc-id] {}))
@@ -775,6 +812,7 @@
775812
(run-lifecycle-fns app-context s/start "start" ordered-services)
776813
(enable-ready-notifications-for-app! services-by-id)
777814
(notice-service-ready-if-uncoordinated! services-by-id)
815+
(log/info (i18n/trs "Finished restart for TK {0}" (app-log-id this)))
778816
(inc-restart-counter! this)
779817
this
780818
(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)