From 03011b8fe68b945e383f335913564d086509e36b Mon Sep 17 00:00:00 2001 From: Marius Ciepluch <11855163+norandom@users.noreply.github.com> Date: Tue, 22 Sep 2026 23:33:23 +0200 Subject: [PATCH] fix(scan): give the PARTITIONED graph a single-attempt path too run_batch_once was wired on the disposable and sharded paths but not the partitioned one -- and a multi-language repository, which every WordPress plugin is, builds through the partitioned graph. So the portioner fell back to run_batch there, whose internal split-retry folded into the time it measured: a portion read 956 s and collapsed the sizer, exactly as before the single-attempt fix. The fix had no effect on the one subject it was written for. PartitionGraph now has run_batch_once, dispatching to each sub-graph the same way run_batch does -- the routing is factored into one _dispatch method that takes the per-graph method name. Sub-graphs are built through the disposable path and already carry run_batch_once. The lesson, again: a capability added at one construction site is not added at the others. There are three that build a queryable graph -- disposable, sharded, partitioned -- and the one that mattered for the measurement was the one missed. 1,424 tests pass, nineteen skipped. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo --- src/openultrasast/model/partitions.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/openultrasast/model/partitions.py b/src/openultrasast/model/partitions.py index 48892aa..972dd8c 100644 --- a/src/openultrasast/model/partitions.py +++ b/src/openultrasast/model/partitions.py @@ -91,6 +91,19 @@ class PartitionGraph: return self.run_batch(kind, {"one": params}).get("one") def run_batch(self, kind: str, requests: Mapping[str, Mapping[str, object]]) -> dict[str, list[object]]: + return self._dispatch(kind, requests, "run_batch") + + def run_batch_once(self, kind: str, requests: Mapping[str, Mapping[str, object]]) -> dict[str, list[object]]: + """A single attempt per partition, for the scan-level portioner that retries itself. + + Without this a multi-language repository -- every WordPress plugin -- had no single-attempt path, so + the portioner fell back to `run_batch`, whose internal split-retry folded into the time it measured: + one portion read 956 s and collapsed the sizer. The routing is identical; only the per-graph method + called differs. + """ + return self._dispatch(kind, requests, "run_batch_once") + + def _dispatch(self, kind: str, requests: Mapping[str, Mapping[str, object]], method: str) -> dict[str, list[object]]: answers: dict[str, list[object]] = {} languages = dict.fromkeys(self.path_languages.get(str(params.get("file", "")), "") for params in requests.values()) for language in languages: @@ -102,7 +115,7 @@ class PartitionGraph: selected = {rid: params for rid, params in requests.items() if self.path_languages.get(str(params.get("file", ""))) == language} if not selected: continue - batch = getattr(graph, "run_batch", None) + batch = getattr(graph, method, None) or getattr(graph, "run_batch", None) raw = batch(kind, selected) if callable(batch) else {rid: graph.run(kind, params) for rid, params in selected.items()} if not isinstance(raw, Mapping): continue