Skip to content

Commit 7701a8f

Browse files
committed
perf: Index pending emails in memory for the shared-thread rescue
1 parent 2f5b62e commit 7701a8f

1 file changed

Lines changed: 57 additions & 9 deletions

File tree

src/bone/digest.clj

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -684,6 +684,57 @@
684684
(or (nil? irt)
685685
(boolean (some #(lookup/email-eid db %) (ancestor-mids email))))))
686686

687+
;; In-memory pending index: ancestor-mid-hash => #{pending eids}. A
688+
;; value join on the hash attr is forbidden (see bone.lookup) and
689+
;; scanning every pending email once per processed email is quadratic
690+
;; during an initial build, so the sole DB writer (this JVM daemon;
691+
;; bb scripts are read-only) mirrors the pending set in memory. The
692+
;; index is seeded lazily from the DB and reseeded when the
693+
;; connection changes (tests open fresh DBs). Entries can only go
694+
;; stale towards false positives (an eid whose flag was cleared);
695+
;; `retry-pending-in-shared-thread!` re-checks the flag on pull.
696+
(defonce ^:private pending-index (atom nil)) ; {:conn c :index {hash #{eid}}}
697+
698+
(defn- seed-pending-index
699+
"Index map rebuilt from the pending emails stored in `db`."
700+
[db]
701+
(reduce (fn [m [e pulled]]
702+
(reduce #(update %1 %2 (fnil conj #{}) e)
703+
m (:email/ancestor-mid-hashes pulled)))
704+
{}
705+
(d/q '[:find ?e (pull ?e [:email/ancestor-mid-hashes])
706+
:where [?e :email/pending-thread? true]]
707+
db)))
708+
709+
(defn- pending-index-map!
710+
"Current index map for `conn`, seeding it on first use. `f`, when
711+
given, is applied to the map in the same atomic swap (seeding is a
712+
pure DB read, safe to retry)."
713+
([conn] (pending-index-map! conn identity))
714+
([conn f]
715+
(:index (swap! pending-index
716+
(fn [cur]
717+
(update (if (identical? (:conn cur) conn)
718+
cur
719+
{:conn conn :index (seed-pending-index (d/db conn))})
720+
:index f))))))
721+
722+
(defn- pending-index-add!
723+
"Record `eid` as pending under each of its ancestor mid-hashes."
724+
[conn eid hashes]
725+
(pending-index-map!
726+
conn (fn [m] (reduce #(update %1 %2 (fnil conj #{}) eid) m hashes))))
727+
728+
(defn- pending-index-remove!
729+
"Drop `eid` from the index (its pending flag was retracted)."
730+
[conn eid hashes]
731+
(pending-index-map!
732+
conn (fn [m]
733+
(reduce (fn [acc h]
734+
(let [s (disj (get acc h #{}) eid)]
735+
(if (seq s) (assoc acc h s) (dissoc acc h))))
736+
m hashes))))
737+
687738
;; ---------------------------------------------------------------------------
688739
;; Single-email processing -- orchestrator
689740
;; ---------------------------------------------------------------------------
@@ -766,6 +817,7 @@
766817
(do
767818
(when was-pending?
768819
(d/transact! conn [[:db/retract eid :email/pending-thread? true]])
820+
(pending-index-remove! conn eid (:email/ancestor-mid-hashes email))
769821
(log/info "Cleared pending flag on" message-id))
770822
(let [{parent-eids :all nearest-eids :nearest} (thread-lookup email db)
771823
;; Recover the existing report-eid on retry so Phase 4
@@ -805,6 +857,7 @@
805857
(do (d/transact! conn [{:db/id eid
806858
:email/pending-thread? true
807859
:email/digested-at (Date.)}])
860+
(pending-index-add! conn eid (:email/ancestor-mid-hashes email))
808861
(log/info "Pending:" message-id "-- no ancestor mid in DB"
809862
"(in-reply-to" (:email/in-reply-to email) ")")))))))))
810863

@@ -817,19 +870,14 @@
817870
hashes (cond-> (set (:email/ancestor-mid-hashes email))
818871
own-mid (conj (common/mid-hash own-mid)))]
819872
(when (seq hashes)
820-
;; In-memory filter on one snapshot: a value join on the hash
821-
;; attr is forbidden (see bone.lookup), and pendings are few.
822-
(let [pendings (->> (d/q '[:find ?e (pull ?e [:email/ancestor-mid-hashes])
823-
:where [?e :email/pending-thread? true]]
824-
(d/db conn))
825-
(keep (fn [[e pulled]]
826-
(when (some hashes (:email/ancestor-mid-hashes pulled))
827-
e))))]
873+
(let [index (pending-index-map! conn)
874+
pendings (into #{} (mapcat #(get index %)) hashes)]
828875
(doseq [pending-eid pendings
829876
:let [pending-email (d/pull (d/db conn) email-pull-pattern
830877
pending-eid)]
831878
;; A recursive rescue triggered by an earlier iteration may
832-
;; have already processed this email and cleared its flag.
879+
;; have already processed this email and cleared its flag --
880+
;; and the index only re-checks staleness here, on pull.
833881
:when (:email/pending-thread? pending-email)]
834882
(log/info "Retrying pending email" (:email/message-id pending-email)
835883
"(triggered by" own-mid ")")

0 commit comments

Comments
 (0)