Skip to content

Commit 9bf202b

Browse files
committed
refactor: Batch Datalevin queries and share datetime formatters
1 parent 14e55e6 commit 9bf202b

7 files changed

Lines changed: 128 additions & 69 deletions

File tree

scripts/bark-docs.clj

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -358,18 +358,21 @@
358358
;; Maintainers section
359359
;; ---------------------------------------------------------------------------
360360

361-
(defn- participant-name
362-
"Look up a participant's display name by email for a given source.
363-
Returns the name if found and non-blank, otherwise nil."
364-
[db source-name email]
365-
(let [dq (resolve 'pod.huahaiy.datalevin/q)
366-
k (str source-name ":" (str/lower-case email))]
367-
(when-let [n (dq '[:find ?n .
368-
:in $ ?k
369-
:where [?e :participant/key ?k]
370-
[?e :participant/name ?n]]
371-
db k)]
372-
(when-not (str/blank? n) n))))
361+
(defn- participant-names-for-source
362+
"Return `{lowercase-email -> non-blank name}` for all participants of
363+
`source-name`. Single query, intended for batch lookups."
364+
[db source-name]
365+
(let [dq (resolve 'pod.huahaiy.datalevin/q)]
366+
(->> (dq '[:find ?email ?name
367+
:in $ ?src
368+
:where
369+
[?e :participant/source ?src]
370+
[?e :participant/email ?email]
371+
[?e :participant/name ?name]]
372+
db source-name)
373+
(reduce (fn [acc [email name]]
374+
(if (str/blank? name) acc (assoc acc email name)))
375+
{}))))
373376

374377
(defn- html-escape
375378
"Escape HTML special characters in a string."
@@ -391,14 +394,15 @@
391394
(when source-name
392395
(let [tenures (get-tenures db source-name)
393396
lead (lead-maintainer tenures)
397+
names (participant-names-for-source db source-name)
394398
;; Sort: lead first, then alphabetical by email (case-insensitive).
395399
sort-key (fn [{:keys [email to]}]
396400
[(if (and (nil? to) (= email lead)) 0 1)
397401
(str/lower-case (or email ""))])
398402
ordered (sort-by sort-key tenures)
399403
entries (mapv
400404
(fn [{:keys [email from to]}]
401-
(let [display (or (participant-name db source-name email) email)
405+
(let [display (or (get names (some-> email str/lower-case)) email)
402406
escaped (html-escape display)
403407
range (cond
404408
(and from to)

scripts/bark-export.clj

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -613,14 +613,20 @@
613613
(when (seq data)
614614
(log/info "Wrote" (count data) "reports to" filename)))))
615615

616+
(def ^:private rfc822-formatter
617+
;; DateTimeFormatter is immutable and thread-safe. Pattern matches
618+
;; SimpleDateFormat's "EEE, dd MMM yyyy HH:mm:ss Z" output (e.g.
619+
;; "Mon, 12 May 2026 14:30:45 +0000") -- do not substitute with
620+
;; RFC_1123_DATE_TIME, which emits "GMT" and a non-padded day.
621+
(-> (java.time.format.DateTimeFormatter/ofPattern "EEE, dd MMM yyyy HH:mm:ss Z")
622+
(.withLocale java.util.Locale/ENGLISH)
623+
(.withZone java.time.ZoneOffset/UTC)))
624+
616625
(defn- rfc822-date
617626
"Format a java.util.Date as an RFC 822 date string (for RSS)."
618627
[^java.util.Date d]
619628
(when d
620-
(let [out-fmt (doto (java.text.SimpleDateFormat. "EEE, dd MMM yyyy HH:mm:ss Z"
621-
java.util.Locale/ENGLISH)
622-
(.setTimeZone (java.util.TimeZone/getTimeZone "UTC")))]
623-
(.format out-fmt d))))
629+
(.format rfc822-formatter (.toInstant d))))
624630

625631
(defn- rss-author [m]
626632
(let [email (:from m)

scripts/bark-stats.clj

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -166,35 +166,40 @@
166166
(group-by #(some-> (:report/type %) name))
167167
(into {} (map (fn [[t rs]] [t (count rs)])))))
168168

169-
(defn- make-ym-formatter
170-
"Create a locale-independent yyyy-MM formatter (UTC).
171-
SimpleDateFormat is not thread-safe, so create a fresh instance each time."
172-
^java.text.SimpleDateFormat []
173-
(doto (java.text.SimpleDateFormat. "yyyy-MM" java.util.Locale/ENGLISH)
174-
(.setTimeZone (java.util.TimeZone/getTimeZone "UTC"))))
169+
(def ^:private ym-formatter
170+
;; DateTimeFormatter is immutable and thread-safe, so it can be shared.
171+
(-> (java.time.format.DateTimeFormatter/ofPattern "yyyy-MM")
172+
(.withLocale java.util.Locale/ENGLISH)
173+
(.withZone java.time.ZoneOffset/UTC)))
175174

176175
(defn- date->ym
177176
"Extract 'yyyy-MM' from a java.util.Date using a locale-independent formatter.
178177
Returns nil on failure."
179178
[date]
180179
(when date
181-
(try (.format (make-ym-formatter) date)
180+
(try (.format ym-formatter (.toInstant ^java.util.Date date))
182181
(catch Exception _ nil))))
183182

184183
(defn- current-ym
185184
"Return [year month] for right now, locale-independent."
186185
[]
187-
(let [s (.format (make-ym-formatter) (java.util.Date.))
186+
(let [s (.format ym-formatter (.toInstant (java.util.Date.)))
188187
[year month] (str/split s #"-")]
189188
[(parse-long year) (parse-long month)]))
190189

190+
(def ^:private last-12-months*
191+
;; Memoize: the script runs once per invocation, so freezing the
192+
;; window at first deref is fine and avoids recomputing 4+ times.
193+
(delay
194+
(let [[cy cm] (current-ym)]
195+
(vec (for [i (range 11 -1 -1)]
196+
(let [total (+ (* cy 12) (dec cm) (- i))]
197+
(format "%04d-%02d" (quot total 12) (inc (mod total 12)))))))))
198+
191199
(defn- last-12-months
192200
"Return a vector of 12 \"yyyy-MM\" strings ending with the current month."
193201
[]
194-
(let [[cy cm] (current-ym)]
195-
(vec (for [i (range 11 -1 -1)]
196-
(let [total (+ (* cy 12) (dec cm) (- i))]
197-
(format "%04d-%02d" (quot total 12) (inc (mod total 12))))))))
202+
@last-12-months*)
198203

199204
(defn- cumulative-by-month
200205
"Given a {\"yyyy-MM\" count} frequency map and a base count (entries before

src/bark/commands.clj

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -763,12 +763,39 @@
763763
"Build a partial pull map exposing relation setters under `:setter-attr`
764764
so `scope-permits?` can resolve `:setter-or-maintainer` on relation
765765
unset directives. `rows` is a seq carrying `:kind`, `:role` and
766-
`:setter-attr`."
766+
`:setter-attr`.
767+
768+
Batches the per-row d/q calls into at most two queries (one per role)
769+
to avoid N+1 lookups when several rows share a role."
767770
[db report-eid rows]
768-
(into {} (keep (fn [{:keys [kind role setter-attr]}]
769-
(when-let [s (relation-setter db report-eid kind role)]
770-
[setter-attr {:email/author-address s}])))
771-
rows))
771+
(let [by-role (group-by :role rows)
772+
from-kinds (some->> (get by-role :current-as-from) seq (map :kind) vec)
773+
to-kinds (some->> (get by-role :current-as-to) seq (map :kind) vec)
774+
from-setters (when from-kinds
775+
(into {} (d/q '[:find ?kind ?setter
776+
:in $ ?from [?kind ...]
777+
:where
778+
[?e :rel/from ?from]
779+
[?e :rel/kind ?kind]
780+
[?e :rel/active? true]
781+
[?e :rel/setter ?setter]]
782+
db report-eid from-kinds)))
783+
to-setters (when to-kinds
784+
(into {} (d/q '[:find ?kind ?setter
785+
:in $ ?to [?kind ...]
786+
:where
787+
[?e :rel/to ?to]
788+
[?e :rel/kind ?kind]
789+
[?e :rel/active? true]
790+
[?e :rel/setter ?setter]]
791+
db report-eid to-kinds)))]
792+
(into {}
793+
(keep (fn [{:keys [kind role setter-attr]}]
794+
(when-let [s (case role
795+
:current-as-from (get from-setters kind)
796+
:current-as-to (get to-setters kind))]
797+
[setter-attr {:email/author-address s}])))
798+
rows)))
772799

773800
(def ^:private closure-relation-rows
774801
"Specs for directive-driven closure relations (Superseded-by /

src/bark/digest.clj

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -289,27 +289,37 @@
289289
n (parse-version-number new-version)]
290290
(when (and n (>= n 1))
291291
(let [versions-to-close (cond-> #{new-version}
292-
(> n 1) (conj (str "v" (dec n))))]
293-
(doseq [rid nearest-report-eids]
294-
;; Refresh the snapshot per-iteration so prior transacts in
295-
;; this loop are visible to the :report/closed check below.
296-
(let [r (d/pull (d/db conn)
297-
[:report/type :report/version :report/topic-value :report/closed
298-
:report/message-id]
299-
rid)]
300-
(when (and (= :patch (:report/type r))
301-
(contains? versions-to-close (:report/version r))
302-
(not (:report/closed r))
303-
(or (and (nil? new-topic) (nil? (:report/topic-value r)))
304-
(and new-topic
305-
(= (str/lower-case new-topic)
306-
(str/lower-case (or (:report/topic-value r) ""))))))
307-
(auto-supersede-patch!
308-
conn rid report-eid email
309-
(str "[PATCH " (:report/version r)
310-
(when-let [t (:report/topic-value r)] (str " " t)) "] "
311-
"(" (:report/message-id r) ") "
312-
"(superseded by " new-version ")")))))))))
292+
(> n 1) (conj (str "v" (dec n))))
293+
new-topic-lc (some-> new-topic str/lower-case)
294+
;; Single snapshot: nearest-report-eids contains distinct
295+
;; rids and the daemon is single-threaded on this section,
296+
;; so no concurrent mutation of :report/closed can happen
297+
;; between pulls. If that invariant changes, restore the
298+
;; per-iteration refresh.
299+
db (d/db conn)
300+
candidates (keep
301+
(fn [rid]
302+
(let [r (d/pull db
303+
[:report/type :report/version
304+
:report/topic-value :report/closed
305+
:report/message-id]
306+
rid)]
307+
(when (and (= :patch (:report/type r))
308+
(contains? versions-to-close
309+
(:report/version r))
310+
(not (:report/closed r))
311+
(= new-topic-lc
312+
(some-> (:report/topic-value r)
313+
str/lower-case)))
314+
[rid r])))
315+
nearest-report-eids)]
316+
(doseq [[rid r] candidates]
317+
(auto-supersede-patch!
318+
conn rid report-eid email
319+
(str "[PATCH " (:report/version r)
320+
(when-let [t (:report/topic-value r)] (str " " t)) "] "
321+
"(" (:report/message-id r) ") "
322+
"(superseded by " new-version ")")))))))
313323

314324
(defn- normalize-subject
315325
"Strip Re:/Fwd: prefixes and bracketed tags to get the base subject."
@@ -547,9 +557,11 @@
547557
email-eid (:db/id email)
548558
from-addr (:email/author-address email)
549559
addr-lc (some-> from-addr str/lower-case)
550-
targets (rel/active-targets db patch-report-eid :resolves)]
560+
targets (rel/active-targets db patch-report-eid :resolves)
561+
patch-mid (:report/message-id
562+
(d/pull db [:report/message-id] patch-report-eid))]
551563
(doseq [bug-eid targets]
552-
(let [bug-state (d/pull db [:report/acked :report/owned] bug-eid)
564+
(let [bug-state (d/pull db [:report/acked :report/owned :report/message-id] bug-eid)
553565
credit (fn [tx attr addr-attr]
554566
(cond-> tx
555567
(nil? (get bug-state attr))
@@ -561,9 +573,8 @@
561573
(d/transact! conn tx)
562574
(tracking/bump-report-updated! conn bug-eid)
563575
(log/info "Auto-credit:" from-addr "credited as acked+owned of"
564-
(:report/message-id (d/pull (d/db conn) [:report/message-id] bug-eid))
565-
"via patch"
566-
(:report/message-id (d/pull (d/db conn) [:report/message-id] patch-report-eid))))))))
576+
(:report/message-id bug-state)
577+
"via patch" patch-mid))))))
567578

568579
(defn- run-post-creation-hooks!
569580
"Execute post-creation side effects driven by the plan."

src/bark/relations.clj

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -341,10 +341,16 @@
341341
bugs (active-targets db patch-eid :resolves)]
342342
(case close-reason
343343
:resolved
344-
(doseq [bug-eid bugs]
345-
(let [closed? (some? (:report/closed
346-
(d/pull (d/db conn) [:report/closed] bug-eid)))]
347-
(when-not closed?
344+
;; Bugs already closed are filtered up-front in a single query.
345+
;; Safe with one snapshot: each bug-eid is distinct, so closing
346+
;; bug-A does not affect the closed status of bug-B.
347+
(let [closed-bugs (when (seq bugs)
348+
(set (d/q '[:find [?b ...]
349+
:in $ [?b ...]
350+
:where [?b :report/closed _]]
351+
db bugs)))]
352+
(doseq [bug-eid bugs]
353+
(when-not (contains? closed-bugs bug-eid)
348354
(d/transact! conn [{:db/id bug-eid
349355
:report/closed email-eid
350356
:report/close-reason :resolved}]))))

src/bark/series.clj

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -145,11 +145,11 @@
145145
:where [?s :series/cover-letter ?e]
146146
[?e :email/message-id ?mid]]
147147
db existing-series))
148-
parent-mids (set (keep (fn [rid]
149-
(d/q '[:find ?mid . :in $ ?r
150-
:where [?r :report/message-id ?mid]]
151-
db rid))
152-
parent-report-eids))]
148+
parent-mids (when (seq parent-report-eids)
149+
(set (d/q '[:find [?mid ...]
150+
:in $ [?r ...]
151+
:where [?r :report/message-id ?mid]]
152+
db parent-report-eids)))]
153153
(some old-mids parent-mids)))]
154154
(when (and restart? ancestor?)
155155
(doseq [sid existing-series]

0 commit comments

Comments
 (0)