Repository navigation
Conversation
Adds dojo.query_hooks: a registry of default-queryset filters keyed by model, applied by the HookedManager declared as `objects` on Product_Type, Product, Engagement, Test, Finding and the rows that hang off them. With nothing registered the querysets are unchanged. Each of those models also gets an explicit `unfiltered` manager, and two nullable columns (archived_at, archive_batch) that open source never reads or writes, with partial indexes built concurrently. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
test_endpoint_queries compares Endpoint_Status.values() against literal rows, which now include the two nullable columns. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Measured on 10 million findings, 0304 blocked every read of dojo_finding for about 40 seconds. Its SQL takes milliseconds, but Django rebuilds its model state after each of the migration's operations, and each table's ACCESS EXCLUSIVE lock is taken by its ALTER and held through all the rebuilding that follows until the migration commits. The operations are now applied to the migration state only, and the database work is the SQL they generate (from sqlmigrate), frozen and run as one batch at the end. The resulting schema is identical; the slowest concurrent read during the migration dropped to 0.4 s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eting them register_excess_duplicate_handler installs one handler for the chunks the excess-duplicate task removes. It runs inside the chunk's transaction with the rows locked; when it returns True nothing is deleted. With no handler, or one that returns False, the chunk is deleted as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Adds a small extension point for plugins: a registry of default-queryset filters.
dojo/query_hooks.pykeeps filters keyed by model label.HookedManager(andHookedBaseManagerforBaseModelsubclasses) applies every filter registered for its model to the queryset it returns. With nothing registered, the queryset is unchanged, so open source behaves exactly as before.objectsbecomes a hooked manager onProduct_Type,Product,Engagement,Test,Findingand the rows that hang off them (endpoint and location statuses and references, finding groups, Jira issues and projects, test imports, vulnerability ids, DojoMeta, app analysis, languages, engagement presets, API scan configurations, tool settings, object products). Becauseobjectsis declared first it is also the default manager, so a registered filter reaches reverse relations,get()and uniqueness checks. Each of these models also gets anunfilteredmanager as the explicit bypass._base_managerstays a plain manager, so forward foreign key access and the deletion collector are never filtered.The same models gain two nullable columns,
archived_atandarchive_batch. Open source never reads or writes them. Migration0304adds the columns (nullable, no default, so no table rewrite) and0305builds partial indexes onarchive_batchwithCREATE INDEX CONCURRENTLYin a non-atomic migration, sodojo_findinganddojo_endpoint_statusare not locked during the build.0304applies its operations to the migration state only and runs the SQL they generate (frozen fromsqlmigrate) as one batch at the end. On a database with 10 million findings, the plain version blocked every read ofdojo_findingfor about 40 seconds: the SQL itself takes milliseconds, but Django rebuilds its model state after each operation, and each table's lock is held from itsALTERuntil the migration commits. With the SQL run last, the longest wait for a concurrent read was 0.4 seconds. The resulting schema is the same.A second, smaller hook lets a plugin set excess duplicates aside instead of deleting them.
register_excess_duplicate_handlerindojo/tasks.pyinstalls one handler for the chunks the excess-duplicate task (Delete Duplicate Findings) removes. The task calls it inside each chunk's transaction, after its import and row locks are taken, and deletes nothing when it returns True. With no handler, or one that returns False, the chunk is deleted exactly as before.Merges on its own
unittests/test_query_hooks.pychecks exactly that on an install with no plugin.register_auth_filter: a plugin registers into a registry at startup rather than patching open source code.CREATE INDEX CONCURRENTLY. With nothing registered, the manager adds one dict lookup per query.Testing
unittests/test_query_hooks.py: with nothing registered every queryset is unchanged; the hooked manager is the default manager and the base manager is not; a registered filter applies to.objects,get()and reverse relations whileunfilteredand_base_managerbypass it; registering the same key again replaces the filter. Run withmanage.py test unittests.test_query_hookson an install without any plugin: 4 passed.unittests/test_excess_duplicate_handler.py: with no handler the chunk is deleted; a handler that returns True stops the delete; one that returns False leaves it in place.makemigrations --checkreports no changes.ruff --config ruff.tomlis clean on the changed files.