|
684 | 684 | (or (nil? irt) |
685 | 685 | (boolean (some #(lookup/email-eid db %) (ancestor-mids email)))))) |
686 | 686 |
|
| 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 | + |
687 | 738 | ;; --------------------------------------------------------------------------- |
688 | 739 | ;; Single-email processing -- orchestrator |
689 | 740 | ;; --------------------------------------------------------------------------- |
|
766 | 817 | (do |
767 | 818 | (when was-pending? |
768 | 819 | (d/transact! conn [[:db/retract eid :email/pending-thread? true]]) |
| 820 | + (pending-index-remove! conn eid (:email/ancestor-mid-hashes email)) |
769 | 821 | (log/info "Cleared pending flag on" message-id)) |
770 | 822 | (let [{parent-eids :all nearest-eids :nearest} (thread-lookup email db) |
771 | 823 | ;; Recover the existing report-eid on retry so Phase 4 |
|
805 | 857 | (do (d/transact! conn [{:db/id eid |
806 | 858 | :email/pending-thread? true |
807 | 859 | :email/digested-at (Date.)}]) |
| 860 | + (pending-index-add! conn eid (:email/ancestor-mid-hashes email)) |
808 | 861 | (log/info "Pending:" message-id "-- no ancestor mid in DB" |
809 | 862 | "(in-reply-to" (:email/in-reply-to email) ")"))))))))) |
810 | 863 |
|
|
817 | 870 | hashes (cond-> (set (:email/ancestor-mid-hashes email)) |
818 | 871 | own-mid (conj (common/mid-hash own-mid)))] |
819 | 872 | (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)] |
828 | 875 | (doseq [pending-eid pendings |
829 | 876 | :let [pending-email (d/pull (d/db conn) email-pull-pattern |
830 | 877 | pending-eid)] |
831 | 878 | ;; 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. |
833 | 881 | :when (:email/pending-thread? pending-email)] |
834 | 882 | (log/info "Retrying pending email" (:email/message-id pending-email) |
835 | 883 | "(triggered by" own-mid ")") |
|
0 commit comments