mirror of
https://github.com/jlengrand/bugsink.git
synced 2026-03-10 08:01:17 +00:00
Issue Paginator: don't attempt to count the Issues
Counting incurs looking at all records which is too expensive if you have e.g. 1_000_000 issues. Note that we take a different approach than the one for Events (where we count-with-timeout). Reason for switching: https://sqlite.org/forum/forumpost/fa65709226 For Events we have a known count for the non-query case (denormalized/counted value), so we preserve what we had there. For Issues the trouble of keeping counts right for muted/etc. is not (currently) worth it.
This commit is contained in:
@@ -200,22 +200,22 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-6 h-6 text-slate-200"><path fill-rule="evenodd" d="M9.78 4.22a.75.75 0 0 1 0 1.06L7.06 8l2.72 2.72a.75.75 0 1 1-1.06 1.06L5.47 8.53a.75.75 0 0 1 0-1.06l3.25-3.25a.75.75 0 0 1 1.06 0Z" clip-rule="evenodd" /></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-6 h-6 text-slate-200"><path fill-rule="evenodd" d="M9.78 4.22a.75.75 0 0 1 0 1.06L7.06 8l2.72 2.72a.75.75 0 1 1-1.06 1.06L5.47 8.53a.75.75 0 0 1 0-1.06l3.25-3.25a.75.75 0 0 1 1.06 0Z" clip-rule="evenodd" /></svg>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if page_obj.paginator.num_pages > 1 %}
|
{% if page_obj.object_list|length > 0 %}
|
||||||
Issues {{ page_obj.start_index|intcomma }}–{{ page_obj.end_index|intcomma }} of {{ page_obj.paginator.count|intcomma }}
|
Issues {{ page_obj.start_index|intcomma }} – {{ page_obj.end_index|intcomma }}
|
||||||
{% elif page_obj.paginator.count > 0 %}
|
{% else %}
|
||||||
{{ page_obj.paginator.count|intcomma }} Issues
|
{% if page_obj.number > 1 %}
|
||||||
|
Less than {{ page_obj.start_index }} Issues {# corresponds to the 1/250 case of having an exactly full page and navigating to an empty page after that #}
|
||||||
|
{% else %}
|
||||||
|
0 Issues
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if page_obj.has_next %}
|
{% if page_obj.has_next %}
|
||||||
<a href="?{% add_to_qs page=page_obj.next_page_number %}" class="inline-flex" title="Next page">
|
<a href="?{% add_to_qs page=page_obj.next_page_number %}" class="inline-flex" title="Next page">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-6 h-6"><path fill-rule="evenodd" d="M6.22 4.22a.75.75 0 0 1 1.06 0l3.25 3.25a.75.75 0 0 1 0 1.06l-3.25 3.25a.75.75 0 0 1-1.06-1.06L8.94 8 6.22 5.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd" /></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-6 h-6"><path fill-rule="evenodd" d="M6.22 4.22a.75.75 0 0 1 1.06 0l3.25 3.25a.75.75 0 0 1 0 1.06l-3.25 3.25a.75.75 0 0 1-1.06-1.06L8.94 8 6.22 5.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd" /></svg>
|
||||||
</a>
|
</a>
|
||||||
<a href="?{% add_to_qs page=page_obj.paginator.num_pages %}" class="inline-flex" title="Last page">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-6 h-6"><path fill-rule="evenodd" d="M12.78 7.595a.75.75 0 0 1 0 1.06l-3.25 3.25a.75.75 0 0 1-1.06-1.06l2.72-2.72-2.72-2.72a.75.75 0 0 1 1.06-1.06l3.25 3.25Zm-8.25-3.25 3.25 3.25a.75.75 0 0 1 0 1.06l-3.25 3.25a.75.75 0 0 1-1.06-1.06l2.72-2.72-2.72-2.72a.75.75 0 0 1 1.06-1.06Z" clip-rule="evenodd" /></svg>
|
|
||||||
</a>
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-6 h-6 text-slate-200"><path fill-rule="evenodd" d="M6.22 4.22a.75.75 0 0 1 1.06 0l3.25 3.25a.75.75 0 0 1 0 1.06l-3.25 3.25a.75.75 0 0 1-1.06-1.06L8.94 8 6.22 5.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd" /></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-6 h-6 text-slate-200"><path fill-rule="evenodd" d="M6.22 4.22a.75.75 0 0 1 1.06 0l3.25 3.25a.75.75 0 0 1 0 1.06l-3.25 3.25a.75.75 0 0 1-1.06-1.06L8.94 8 6.22 5.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd" /></svg>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-6 h-6 text-slate-200"><path fill-rule="evenodd" d="M12.78 7.595a.75.75 0 0 1 0 1.06l-3.25 3.25a.75.75 0 0 1-1.06-1.06l2.72-2.72-2.72-2.72a.75.75 0 0 1 1.06-1.06l3.25 3.25Zm-8.25-3.25 3.25 3.25a.75.75 0 0 1 0 1.06l-3.25 3.25a.75.75 0 0 1-1.06-1.06l2.72-2.72-2.72-2.72a.75.75 0 0 1 1.06-1.06Z" clip-rule="evenodd" /></svg>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ from django.http import Http404
|
|||||||
from django.core.paginator import Paginator, Page
|
from django.core.paginator import Paginator, Page
|
||||||
from django.db.utils import OperationalError
|
from django.db.utils import OperationalError
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.utils.functional import cached_property
|
||||||
|
|
||||||
from sentry.utils.safe import get_path
|
from sentry.utils.safe import get_path
|
||||||
from sentry_sdk_extensions import capture_or_log_exception
|
from sentry_sdk_extensions import capture_or_log_exception
|
||||||
@@ -87,6 +88,35 @@ class KnownCountPaginator(EagerPaginator):
|
|||||||
return self._count
|
return self._count
|
||||||
|
|
||||||
|
|
||||||
|
class UncountablePage(Page):
|
||||||
|
"""The Page subclass to be used with UncountablePaginator."""
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def has_next(self):
|
||||||
|
# hack that works 249/250 times: if the current page is full, we have a next page
|
||||||
|
return len(self.object_list) == self.paginator.per_page
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def end_index(self):
|
||||||
|
return (self.paginator.per_page * (self.number - 1)) + len(self.object_list)
|
||||||
|
|
||||||
|
|
||||||
|
class UncountablePaginator(EagerPaginator):
|
||||||
|
"""optimization: counting is too expensive; to be used in a template w/o .count and .last"""
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def _get_page(self, *args, **kwargs):
|
||||||
|
object_list = args[0]
|
||||||
|
object_list = list(object_list)
|
||||||
|
return UncountablePage(object_list, *(args[1:]), **kwargs)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def count(self):
|
||||||
|
return 1_000_000_000 # big enough to be bigger than what you can click through or store in the DB.
|
||||||
|
|
||||||
|
|
||||||
def _request_repr(parsed_data):
|
def _request_repr(parsed_data):
|
||||||
if "request" not in parsed_data:
|
if "request" not in parsed_data:
|
||||||
return ""
|
return ""
|
||||||
@@ -268,7 +298,7 @@ def _issue_list_pt_2(request, project, state_filter, unapplied_issue_ids):
|
|||||||
if request.GET.get("q"):
|
if request.GET.get("q"):
|
||||||
issue_list = search_issues(project, issue_list, request.GET["q"])
|
issue_list = search_issues(project, issue_list, request.GET["q"])
|
||||||
|
|
||||||
paginator = EagerPaginator(issue_list, 250)
|
paginator = UncountablePaginator(issue_list, 250)
|
||||||
page_number = request.GET.get("page")
|
page_number = request.GET.get("page")
|
||||||
page_obj = paginator.get_page(page_number)
|
page_obj = paginator.get_page(page_number)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user