[PATCH RFC v2 05/25] review-tui: recompute an ev icted A·R·T cache entry

Christian Brauner <[email protected]>
Newsgroups org.kernel.linux.tools
Message-ID <[email protected]>
_invalidate_caches(change_id) evicts a single series from the A·R·T count
cache, but _load_series() only refills that cache when the whole dict is
None, so the evicted entry is never recomputed.  The series dicts are
rebuilt from scratch on every load, so nothing carries the old value
forward either, and the series renders '-' in the A·R·T column for the
rest of the session.

Take, link, upgrade, snooze, unsnooze, waiting and thank all evict
exactly one entry, so every one of them hits this.

Refill whenever a wanted branch is missing from the cache rather than
only when the cache is gone, and merge rather than replace so an intact
cache still skips the subprocess.

Signed-off-by: Christian Brauner (Amutable) <[email protected]>
---
 src/b4/review_tui/_tracking_app.py | 34 +++++++++++++++++++++++++++++++---
 1 file changed, 31 insertions(+), 3 deletions(-)

diff --git a/src/b4/review_tui/_tracking_app.py b/src/b4/review_tui/_tracking_app.py
index 69da8f58..11e79ef2 100644
--- a/src/b4/review_tui/_tracking_app.py
+++ b/src/b4/review_tui/_tracking_app.py
@@ -988,7 +988,11 @@ class TrackingApp(LoreNodeShutdownMixin, CheckRunnerMixin, App[Optional[str]]):
         self._cached_newest_revisions: Optional[Dict[str, int]] = None
         self._cached_revision_counts: Optional[Dict[str, int]] = None
         self._cached_revisions: Optional[Dict[str, List[Dict[str, Any]]]] = None
-        self._cached_art_counts: Optional[Dict[str, Tuple[int, int, int]]] = None
+        # A None value is a branch whose tip carries no tracking trailer
+        # block; cached as a miss so the refill below converges.
+        self._cached_art_counts: Optional[Dict[str, Optional[Tuple[int, int, int]]]] = (
+            None
+        )
 
     def _invalidate_caches(self, change_id: Optional[str] = None) -> None:
         """Drop cached data so the next _load_series re-fetches.
@@ -1190,8 +1194,32 @@ class TrackingApp(LoreNodeShutdownMixin, CheckRunnerMixin, App[Optional[str]]):
                     art_branches[branch_name] = branch_tips[branch_name]
 
         # --- Bulk ART counts (1 subprocess instead of N) ---
-        if self._cached_art_counts is None and art_branches and topdir:
-            self._cached_art_counts = _get_art_counts_batch(topdir, art_branches)
+        # Recompute whenever a wanted branch is missing, not only when the
+        # whole dict is gone: _invalidate_caches(change_id) evicts a single
+        # entry, which an `is None` test would never notice, leaving that
+        # series' A·R·T stuck at '-' for the session.
+        #
+        # Only the missing ones, though.  The targeted eviction keeps the
+        # other entries precisely so they are not recomputed, and handing
+        # the whole map to the batch spends that back: one tracking commit
+        # read per branch under review, on every take, link, snooze or
+        # thank, to re-derive counts nothing has invalidated.
+        art_missing = {
+            name: sha
+            for name, sha in art_branches.items()
+            if not self._cached_art_counts or name not in self._cached_art_counts
+        }
+        if topdir and art_missing:
+            counts = _get_art_counts_batch(topdir, art_missing)
+            if self._cached_art_counts is None:
+                self._cached_art_counts = {}
+            self._cached_art_counts.update(counts)
+            # Misses recorded too, or a branch _get_art_counts_batch declines
+            # to return stays absent, the difference above never empties and
+            # a `git cat-file` fires on every reload -- once a second while a
+            # cron sweep bumps the DB mtime.
+            for branch_name in art_missing:
+                self._cached_art_counts.setdefault(branch_name, None)
         art_map = self._cached_art_counts or {}
         for series in self._all_series:
             change_id = series.get('change_id', '')

-- 
2.53.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.