Skip to content

Optimize some management endpoints via the querysets and prefetches#1600

Merged
MyPyDavid merged 30 commits into
2.4.5/releasefrom
2.4.5/fix/optimize-management-querysets
May 27, 2026
Merged

Optimize some management endpoints via the querysets and prefetches#1600
MyPyDavid merged 30 commits into
2.4.5/releasefrom
2.4.5/fix/optimize-management-querysets

Conversation

@MyPyDavid
Copy link
Copy Markdown
Member

@MyPyDavid MyPyDavid commented May 7, 2026

Description

Related issue: "I think we had an issue from even before the migration to the React interface"

Some managment pages were still loading slowly, one prefetch field default_option for the questions.Models was missing. This may improve some queries.

MyPyDavid added 2 commits May 7, 2026 09:13
…and exports

Signed-off-by: David Wallace <david.wallace@tu-darmstadt.de>
…or catalog index

Signed-off-by: David Wallace <david.wallace@tu-darmstadt.de>
@MyPyDavid MyPyDavid added this to the RDMO 2.4.5 milestone May 7, 2026
@MyPyDavid MyPyDavid self-assigned this May 7, 2026
@MyPyDavid MyPyDavid changed the title Optimize management querysets Optimize some management querysets May 7, 2026
@MyPyDavid MyPyDavid changed the base branch from main to 2.4.5/release May 7, 2026 07:47
@coveralls
Copy link
Copy Markdown

coveralls commented May 7, 2026

Coverage Status

Coverage is 95.101%2.4.5/fix/optimize-management-querysets into 2.4.5/release. No base build found for 2.4.5/release.

MyPyDavid added 3 commits May 7, 2026 14:23
…sets

Signed-off-by: David Wallace <david.wallace@tu-darmstadt.de>
Signed-off-by: David Wallace <david.wallace@tu-darmstadt.de>
Signed-off-by: David Wallace <david.wallace@tu-darmstadt.de>
"queries": {
"index": 3,
"list": 9,
"detail": 9,
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this mapper defines the max query counts per api endpoint action during a test.
Very simple test at the moment. They could be expanded by creating 10000 Options, Attributes, etc dynamically during test as well..

Comment thread rdmo/views/viewsets.py
'editors'
)

def get_queryset(self):
Copy link
Copy Markdown
Member Author

@MyPyDavid MyPyDavid May 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nearly forgot the Tasks and Views ;)

Copy link
Copy Markdown
Member

@jochenklar jochenklar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am a bit puzzled, why I cannot follow part of your optimizations, maybe we should talks next week.

Comment thread rdmo/domain/viewsets.py Outdated
Comment thread rdmo/domain/viewsets.py
Comment thread rdmo/management/tests/test_management_api_performance.py Outdated
Comment thread rdmo/management/tests/test_management_api_performance.py Outdated
Comment thread rdmo/questions/models/catalog.py Outdated
'catalog_sections__section__section_pages__page__page_questions__question__attribute',
'catalog_sections__section__section_pages__page__page_questions__question__conditions',
'catalog_sections__section__section_pages__page__page_questions__question__optionsets',
'catalog_sections__section__section_pages__page__page_questions__question__default_option',
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not convinced prefetching the default_option is needed, since we only use its id in the serializer and do not resolve the instance.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And what about the QuestionExportSerializer? It uses default_option = serializers.CharField(source='default_option.uri', default=None, read_only=True) so default_option.uri needs to be resolved for exports right?! It would be used by all Export serializers up to the CatalogExport. Would it be worth to add the prefetch field only for export or apply it only for the export actions? Otherwise we can leave it out like you said.

Copy link
Copy Markdown
Member Author

@MyPyDavid MyPyDavid May 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So actually, the export of a Catalog via the management interface can make almost 3000(!) queries and debug toolbar crashed my machine a bit.
Is it because of a missing prefetch_elements call in the detail_export?

Comment thread rdmo/questions/viewsets.py Outdated
'questionsets',
'editors'
).select_related('attribute')
).select_related('attribute', 'default_option')
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above, I don't think we use this.

…elated

Signed-off-by: David Wallace <david.wallace@tu-darmstadt.de>
@MyPyDavid MyPyDavid changed the title Optimize some management querysets Optimize some management endpoints via the querysets May 12, 2026
MyPyDavid added 5 commits May 13, 2026 10:30
Signed-off-by: David Wallace <david.wallace@tu-darmstadt.de>
Signed-off-by: David Wallace <david.wallace@tu-darmstadt.de>
Signed-off-by: David Wallace <david.wallace@tu-darmstadt.de>
Signed-off-by: David Wallace <david.wallace@tu-darmstadt.de>
…unctinos and select_related calls

Signed-off-by: David Wallace <david.wallace@tu-darmstadt.de>
@MyPyDavid
Copy link
Copy Markdown
Member Author

so, these were the query counts from before the optimizations of the export and detail_export actions:

 Error: test_page_endpoints_query_counts[detail_export-13-url_kwargs4]

Failed: Expected to perform 13 queries or less but 20 were done (add -v option to show queries)
Error: test_views_endpoints_query_counts[detail_export-10-url_kwargs4]

Failed: Expected to perform 10 queries or less but 12 were done (add -v option to show queries)
Error: test_page_endpoints_query_counts[export-25-url_kwargs2]

Failed: Expected to perform 25 queries or less but 409 were done (add -v option to show queries)
Error: test_question_endpoints_query_counts[export-12-url_kwargs2]

Failed: Expected to perform 12 queries or less but 398 were done (add -v option to show queries)
Error: test_optionset_endpoints_query_counts[detail_export-10-url_kwargs4]

Failed: Expected to perform 10 queries or less but 14 were done (add -v option to show queries)
Error: test_actions_max_query_counts[export-11-url_kwargs2]

Failed: Expected to perform 11 queries or less but 26 were done (add -v option to show queries)
Error: test_actions_max_query_counts[detail_export-9-url_kwargs4]

Failed: Expected to perform 9 queries or less but 17 were done (add -v option to show queries)
Error: test_catalog_endpoints_query_counts[detail_export-29-url_kwargs4]

Failed: Expected to perform 29 queries or less but 425 were done (add -v option to show queries)
Error: test_catalog_endpoints_query_counts[export-30-url_kwargs2]

Failed: Expected to perform 30 queries or less but 408 were done (add -v option to show queries)
.F.........F..F..............FF........................F.FF.........F... [ 99%]
Error: test_questionset_endpoints_query_counts[export-17-url_kwargs2]

Failed: Expected to perform 17 queries or less but 70 were done (add -v option to show queries)
Error: test_questionset_endpoints_query_counts[detail_export-13-url_kwargs4]

Failed: Expected to perform 13 queries or less but 26 were done (add -v option to show queries)
Error: test_section_endpoints_query_counts[export-27-url_kwargs2]

Failed: Expected to perform 27 queries or less but 406 were done (add -v option to show queries)
Error: test_section_endpoints_query_counts[detail_export-12-url_kwargs4]

Failed: Expected to perform 12 queries or less but 59 were done (add -v option to show queries)
Error: test_domain_endpoints_query_counts[detail_export-12-url_kwargs4]

Failed: Expected to perform 12 queries or less but 30 were done (add -v option to show queries)
Error: test_domain_endpoints_query_counts[export-12-url_kwargs2]

Failed: Expected to perform 12 queries or less but 160 were done (add -v option to show queries)
Error: test_domain_endpoints_query_counts[index-3-None]

KeyError: 'index'
Error: test_domain_endpoints_query_counts[list-10-None]

Failed: Expected to perform 10 queries or less but 114 were done (add -v option to show queries)
Error: test_tasks_endpoints_query_counts[detail_export-12-url_kwargs4]

Failed: Expected to perform 12 queries or less but 16 were done (add -v option to show queries)
...F..F.FF.F.F.FF...........F........................................... [ 99%]

For a catalog 😅 :
Expected to perform 29 queries or less but 425 were done

@MyPyDavid MyPyDavid requested a review from jochenklar May 13, 2026 18:57
Comment thread rdmo/conditions/viewsets.py Outdated
from .serializers.v1 import ConditionIndexSerializer, ConditionSerializer


def get_attributes_by_id():
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is not needed because of .in_bulk(), but if it should go into utils.py or managers.py.

Also this prefetches all (!) attributes, which is overkill.

Comment thread rdmo/conditions/viewsets.py Outdated
)

def get_export_renderer_context(self, request):
def get_export_renderer_context(self, request, attributes_by_id=False):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be a seperate get_export_serializer_context to for the serializer context.

Comment thread rdmo/domain/serializers/export.py Outdated
def get_parent_data(self, obj):
if obj.parent is not None:
return AttributeExportSerializer(obj.parent).data
parent = self.get_parent_object_from_context(obj)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those to methods are unnecessary complex, part because AI slob and part because the original code with the parent_data was not great. I refactored the whole thing in 2.4.5/fix/optimize-management-querysets+refactor.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where is this branch 2.4.5/fix/optimize-management-querysets+refactor that you speak of?? 👀 👀

Copy link
Copy Markdown
Member

@jochenklar jochenklar May 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, sorry, I forgot to push: #1616.

Comment thread rdmo/questions/viewsets.py Outdated
)


def get_attributes_by_id():
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above.

Comment thread rdmo/questions/models/catalog.py Outdated
raise RuntimeError('uri_path is missing')
return join_url(uri_prefix or settings.DEFAULT_URI_PREFIX, '/questions/', uri_path)


Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part (same for the other question models) needs more work. I get that the recursion solves the problem with nested questionsets. However for the full export we need the same attribute_map I implemented for the other models in 2.4.5/fix/optimize-management-querysets+refactor.

Nevertheless, all this code does not belong in models.py. Please create an extra prefetch.py for it and refactor the prefetch_elements methods accordingly. Catalog.prefetch_lookups is not needed anymore. child_questionset_prefetch should be questionset_questionset_prefetch to match the other naming in RDMO.

After this we need to see if we need a special case for default_option since it is only needed for the export and would maybe slow down the answer tree.

@MyPyDavid MyPyDavid changed the title Optimize some management endpoints via the querysets Optimize some management endpoints via the querysets and prefetches May 18, 2026
MyPyDavid added 4 commits May 20, 2026 09:51
Signed-off-by: David Wallace <david.wallace@tu-darmstadt.de>
Signed-off-by: David Wallace <david.wallace@tu-darmstadt.de>
Signed-off-by: David Wallace <david.wallace@tu-darmstadt.de>
…ect page

Signed-off-by: David Wallace <david.wallace@tu-darmstadt.de>
MyPyDavid and others added 2 commits May 20, 2026 10:42
Signed-off-by: David Wallace <david.wallace@tu-darmstadt.de>
…ent-querysets+refactor

Refactor queryset optimizations
@MyPyDavid
Copy link
Copy Markdown
Member Author

I've made some changes according to your suggestions and included the refactor PR..

@MyPyDavid MyPyDavid requested a review from jochenklar May 20, 2026 10:00
@jochenklar
Copy link
Copy Markdown
Member

Alright, this was quite a ride, but I think/hope/pray that we have a good result now. I did not get why the Prefect -> select_related pattern is better than .prefetch_related('foo__bar__baz') at first, but it is just one prefetch query less.

  • I refactored prefetch.py to make some of the prefetching optional (the answer tree needs less than the page in the interview and that needs less than the export). I also made it a bit more readable, with the "public" methods on top.
  • get_export_renderer_context is now get_export_flags.
  • The attribute_map pattern is now also used in the questions export.
  • get_section_for_page did something wrong (since a long time ago), which prevented the use of prefetching.
  • I renamed the news tests to test_queries_*.
  • The nested serializers needed some more work, since they inherited from the regular full serializer, which lead to a lot of queries. I created new, stand-alone serializers. I thought about creating a CatalogBaseSerializer, but I think its cleaner this way, since we don't need to change/break the existing serializers.
  • I also moved some getters from the serializers to the models.

@MyPyDavid please check and then merge.

Signed-off-by: David Wallace <david.wallace@tu-darmstadt.de>
@MyPyDavid
Copy link
Copy Markdown
Member Author

ok, thanks @jochenklar! 🙌
I've added the is_collection field to these serializers, that was still missing from them right? bc94b27

@jochenklar
Copy link
Copy Markdown
Member

Yes, thanks!

@MyPyDavid MyPyDavid merged commit 38ca003 into 2.4.5/release May 27, 2026
19 checks passed
@MyPyDavid MyPyDavid deleted the 2.4.5/fix/optimize-management-querysets branch May 27, 2026 15:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants