Repository: mdn/kuma

Issue:
search_phase_execution_exception in ES on huuuge ?page params
https://sentry.prod.mozaws.net/operations/mdn-prod/issues/6620806/

```
TransportError: TransportError(500, u'search_phase_execution_exception', u'Result window is too large, from + size must be less than or equal to: [10000] but was [33010]. See the scroll api for a more efficient way to request large data sets. This limit can be set by changing the [index.max_result_window] index level setting.')
(24 additional frame(s) were not displayed)
...
  File "rest_framework/views.py", line 492, in dispatch
    response = handler(request, *args, **kwargs)
  File "rest_framework/generics.py", line 201, in get
    return self.list(request, *args, **kwargs)
  File "rest_framework/mixins.py", line 42, in list
    page = self.paginate_queryset(queryset)
  File "rest_framework/generics.py", line 173, in paginate_queryset
    return self.paginator.paginate_queryset(queryset, self.request, view=self)
  File "rest_framework/pagination.py", line 204, in paginate_queryset
    self.page = paginator.page(page_number)

TransportError: TransportError(500, u'search_phase_execution_exception', u'Result window is too large, from + size must be less than or equal to: [10000] but was [33010]. See the scroll api for a more efficient way to request large data sets. This limit can be set by changing the [index.max_result_window] index level setting.')
```


Discussion:
You can reproduce this locally by opening http://wiki.mdn.localhost:8000/en-US/search?page=3301

Resolving patch:
diff --git a/kuma/search/paginator.py b/kuma/search/paginator.py
--- a/kuma/search/paginator.py
+++ b/kuma/search/paginator.py
@@ -1,4 +1,5 @@
-from django.core.paginator import EmptyPage, Page, PageNotAnInteger, Paginator
+from django.core.paginator import (
+    EmptyPage, InvalidPage, Page, PageNotAnInteger, Paginator)
 from django.utils.functional import cached_property
 
 
@@ -19,7 +20,7 @@ def validate_number(self, number):
         """
         Validates the given 1-based page number.
 
-        This class overrides the default behavior and ignores the upper bound.
+        We also check that the number isn't too large.
         """
         try:
             number = int(number)
@@ -27,6 +28,19 @@ def validate_number(self, number):
             raise PageNotAnInteger('That page number is not an integer')
         if number < 1:
             raise EmptyPage('That page number is less than 1')
+
+        if number >= 1000:
+            # Anything >=1,000 will result in a hard error in
+            # Elasticsearch which would happen before we even get a chance
+            # to validate that the range is too big. The error you would
+            # get from Elasticsearch 6.x is something like this:
+            #
+            #     Result window is too large, from + size must be less
+            #     than or equal to: [10000] but was [11000].
+            #
+            # See https://github.com/mdn/kuma/issues/6092
+            raise InvalidPage('Page number too large')
+
         return number
 
     def page(self, number):


Test patch:
diff --git a/kuma/search/tests/test_views.py b/kuma/search/tests/test_views.py
--- a/kuma/search/tests/test_views.py
+++ b/kuma/search/tests/test_views.py
@@ -162,6 +162,14 @@ class PaginationSearchView(SearchView):
         response = view(request)
         assert response.data['pages'] == 2
 
+    def test_paginate_too_large(self):
+        response = self.client.get('/en-US/search', {'page': 1000},
+                                   HTTP_HOST=settings.WIKI_HOST)
+        # It's a bit odd that an invalid query string parameter
+        # causes a 404 Not Found but that's how the paginator for
+        # rest_framework works.
+        assert response.status_code == 404
+
     def test_tokenize_camelcase_titles(self):
         for q in ('get', 'element', 'by', 'id'):
             response = self.client.get('/en-US/search', {'q': q},