Repository navigation
perf(reimport): batch the finding group JIRA lookups in process_groups_for_all_findings - #16869
Conversation
…s_for_all_findings With JIRA keep-in-sync on, process_groups_for_all_findings read each group of the reimport and its JIRA issue one query at a time, then read the JIRA issue of every reactivated or unchanged group one query at a time. A reimport touching 200 groups ran 784 statements instead of 241. The first finding of every group name is now loaded once, with its JIRA issue, its group and the group's JIRA issue. The reactivated/unchanged groups are read with their JIRA issue. Their ids now come from two id-list reads per chunk (open findings, then memberships) instead of a DISTINCT subquery under the group read. With planner statistics that saw the group tables as empty, that subquery ran once per group and probed dojo_finding with the whole id list for every membership: 94 s for 200 groups on a 10M-finding database. Which objects are pushed, and in which order, is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Independent review of this change (I did not write it). Text only. Regression tests. Run in a private unit-test container: with this PR's Same results as
In every shape the groups pass pushed the same objects in the same order, and the groups, memberships, JIRA issue rows, finding states, alerts and history rows were identical. On entry to the pass, no first finding had Measurements (5 runs per side, alternating in one process):
The stale state happened without any help, from an autovacuum running while a reimport's uncommitted rows held pages in near-empty group tables. So it could plausibly happen on an instance that has just started using One suggestion. The new tests call |
…t level TestDojoReimportGroupJiraPerformance reimports through DefaultReImporter.process_scan with group_by and a keep-in-sync JIRA instance whose groups have issues, into 2 and into 10 groups with the same findings, and asserts the statement count does not grow with the groups (dev: 122 statements with 2 groups, 146 with 10) and that every group is pushed once per pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Resolution of the review:
Found while writing the test, and unchanged by this PR: a reimport names a new finding's group from the parsed title, while the import named the group from the saved, title-cased title. So a report whose titles are not already title-cased puts the reimport's new findings into a second group with the same name. The new test uses title-cased titles. |
Description
A reimport with
group_byset and JIRA keep-in-sync on (finding_jira_sync) read one finding group and its JIRA issue at a time inDefaultReImporter.process_groups_for_all_findings:findings[0].finding_group(one query) and then the group's JIRA issue insideis_keep_in_sync(one more);A reimport touching 200 groups ran 784 statements where 241 do the same work.
This PR batches those reads and changes nothing about what gets pushed:
select_related("jira_issue")andprefetch_related("finding_group_set__jira_issue").Finding_Grouporders byid, sofinding.finding_group(finding_group_set.all().first()) is served from the prefetch.select_related("jira_issue").DISTINCTsubquery joiningdojo_findingtodojo_finding_group_findingssat under theFinding_Groupread. On a 10M-finding database whose group tables' planner statistics read as empty while they held rows, Postgres planned that as a nested loop that re-ran the subquery for each group. Each run probeddojo_finding_pkeywithid = <membership's finding_id> AND id = ANY(<1,440 ids>)for every membership: 431,572 probes and 94 s for 200 groups. The new reads have no subquery, so that plan cannot happen. We produced those statistics with rolled-back benchmark runs plus an autovacuum, and have not seen them in production.Which objects are pushed to JIRA, and in which order, is the same as before. That includes a lone ungrouped finding pushed on its own JIRA issue, and every group being pushed when
push_to_jirais set.Test results
New
TestDojoReimportGroupJiraPerformanceinunittests/test_importers_performance.py: a reimport throughDefaultReImporter.process_scanwithgroup_byand a keep-in-sync JIRA instance whose groups have issues, into 2 and into 10 groups with the same findings. Withdev'sdefault_reimporter.pyit fails: 124 statements with 2 groups and 148 with 10. With this change both are the same, and both versions push the same groups.New
unittests/test_reimport_finding_group_jira_queries.py, which calls the pass directly:test_group_lookups_do_not_grow_with_the_number_of_groups: ondev, 13 queries for 2 groups and 85 for 20. With this change both are the same. It also checks every pushed object, in order.test_unchanged_groups_are_read_by_id_not_through_a_subquery: fails ondev(theIN (SELECTshape), passes with this change.test_ungrouped_finding_is_pushed_itself_when_kept_in_sync(with and without a JIRA issue) andtest_push_to_jira_pushes_every_group_without_reading_issuespass ondevand with this change: they pin current behavior.Run in the unit-test compose stack, on this branch merged with the latest
dev: the two new test modules plustest_importers_auto_finding_groups,test_reimport_finding_group_duplicates,test_jira_import_and_pushing_api,test_importers_performance,test_reimport_close_old_findings_batch,test_import_reimport,test_importers_importer,test_jira_finding_group_perf,test_reimport_batch_flush,test_reimport_final_drain,test_reimport_group_jira_sync,test_reimport_prefetch,test_reimport_suppressed_locationandtest_reimporter_persist_seam: 312 tests, OK (1 skipped).ruff checkis clean.Measured on a database of 10M findings, reimporting a 2.9 MB Semgrep report (2,520 findings; 1,440 unchanged, 1,080 new, 1,080 closed; 200 groups by title, each with a JIRA issue). Old and new ran in one process, alternating, 5 runs each:
group_by.Documentation
No user-facing change.
🤖 Generated with Claude Code