Skip to content

Commit 5f3e128

Browse files
committed
[mod] [BREAKING] New API for draining stats accumulator
This change is relevant only to users of Tufte's experimental `stats-accumulator` util. Before this commit: @my-stats-accumulator returns {id pstats} and drains the accumulator. After this commit: @my-stats-accumulator returns {id pstats} WITHOUT draining, (my-stats-accumulator) returns {id pstats} and drains the accumulator. I.e. before you'd use @my-sacc to drain. But now you should use (my-sacc) to drain. Motivation for this change: It wasn't previously possible to access accumulator's contents WITHOUT draining. This is now possible, and can be handy. Migration info: If you use the `stats-accumulator` util, please check your code for derefs. These should be changed to invocations instead: @my-stats-accumulator -> (my-stats-accumulator). Sorry for the inconvenience! - Peter Taoussanis
1 parent 15b1d40 commit 5f3e128

File tree

2 files changed

+27
-18
lines changed

2 files changed

+27
-18
lines changed

examples/clj/src/example/server.clj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
(enc/defonce stats-accumulator
1919
"Accumulates results from all unfiltered `profile` calls.
20-
Deref this to get the accumulated data."
20+
Invoke this to drain and return accumulated data."
2121
(tufte/stats-accumulator))
2222

2323
;; Register handler for `profile` results
@@ -55,7 +55,7 @@
5555
[:h1 "Performance report since last call:"]
5656
[:hr]
5757
[:pre
58-
(if-let [m (not-empty @stats-accumulator)]
58+
(if-let [m (not-empty (stats-accumulator))]
5959
(tufte/format-grouped-pstats m)
6060
"Nothing (try visit the /sleep/... endpoints)")]
6161
[:hr]

src/taoensso/tufte.cljc

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -517,28 +517,38 @@
517517

518518
(deftype StatsAccumulator [acc]
519519
;; `acc` - (latom {<profiling-id> <pstats>})
520-
#?(:clj clojure.lang.IDeref :cljs IDeref) (#?(:clj deref :cljs -deref) [_] (enc/reset-in! acc {})) ; Drain
521-
#?(:clj clojure.lang.IFn :cljs IFn) (#?(:clj invoke :cljs -invoke) [_] (enc/reset-in! acc {})) ; Drain
522-
( #?(:clj invoke :cljs -invoke) [_ profiling-id ps]
523-
(when (and profiling-id ps)
524-
;; Contention would be expensive so consumer should serialize calls
525-
(acc profiling-id #(impl/merge-pstats % ps))
526-
true)))
520+
521+
#?(:cljs IDeref :clj clojure.lang.IDeref)
522+
#?(:clj (deref [this] @acc)
523+
:cljs (-deref [this] @acc))
524+
525+
#?(:cljs IFn :clj clojure.lang.IFn)
526+
#?(:clj (invoke [this] (enc/reset-in! acc {}))
527+
:cljs (-invoke [this] (enc/reset-in! acc {})))
528+
529+
;; Contention would be expensive here so consumer may want to serialize calls
530+
#?(:clj (invoke [this id pstats] (when (and id pstats) (locking acc (acc id #(impl/merge-pstats % pstats))) true))
531+
:cljs (-invoke [this id pstats] (when (and id pstats) (acc id #(impl/merge-pstats % pstats)) true))))
532+
533+
(comment
534+
(let [sacc (stats-accumulator)
535+
[_ ps1] (profiled {} (p :foo (Thread/sleep 10)))]
536+
(enc/qb 1e5 (sacc :id1 ps1)))) ; 45.01
527537

528538
(defn stats-accumulator
529539
"Experimental, subject to change. Feedback welcome!
530540
Small util to help merge `pstats` from multiple runs and/or threads.
531541
532542
Returns a stateful `StatsAccumulator` (`sacc`) with:
533-
- (sacc <profiling-id> <pstats>) ; Merges given pstats under given profile id
534-
- @sacc ; Drains accumulator and returns drained
535-
; {<profiling-id> <merged-pstats>}
543+
- (sacc <id> <pstats>) - Merges given pstats under given profiling id
544+
- (sacc) --------------- Returns current {<profiling-id> <merged-pstats>} and drains accumulator
545+
- @sacc ---------------- Returns current {<profiling-id> <merged-pstats>} WITHOUT draining
536546
537-
Note that for performance reasons, you'll likely want some kind of
538-
async/buffer/serialization mechanism in front of merge calls.
547+
For performance you may want to use some sort of serialization
548+
mechanism (e.g. agent) to avoid high contention when merging.
539549
540550
One common pattern using `handler:accumulating` is to create a
541-
system-wide accumulator that you deref every n minutes/etc. to get
551+
system-wide accumulator that you invoke every n minutes/etc. to get
542552
a view of system-wide performance over the period, e.g.:
543553
544554
(defonce my-sacc (stats-accumulator) ; Create an accumulator
@@ -548,7 +558,7 @@
548558
;; Drain and print formatted stats every minute
549559
(future
550560
(while true
551-
(when-let [m (not-empty @my-sacc)]
561+
(when-let [m (not-empty (my-sacc))]
552562
(println (format-grouped-pstats m)))
553563
(Thread/sleep 60000))))
554564
@@ -562,9 +572,8 @@
562572
(enc/qb 1e6 (stats-accumulator)) ; 45.37
563573
(let [sacc (stats-accumulator)]
564574
(sacc :profiled1 (second (profiled {} (p :p1 nil))))
565-
(Thread/sleep 100)
566575
(sacc :profiled2 (second (profiled {} (p :p2 nil))))
567-
[@sacc @sacc]))
576+
[(sacc) (sacc)]))
568577

569578
(comment
570579
(def my-sacc (add-accumulating-handler! {:ns-pattern "*"}))

0 commit comments

Comments
 (0)