request: Weaken ZulipRequestNotes.tornado_handler reference.

This prevents a memory leak arising from Python’s inability to collect
a reference cycle from a WeakKeyDictionary value to its key
(https://bugs.python.org/issue44680).

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg 2021-07-19 14:41:37 -07:00 committed by Tim Abbott
parent 7c32134fb5
commit 6564b258f1
4 changed files with 9 additions and 6 deletions

View File

@ -62,7 +62,9 @@ class ZulipRequestNotes:
error_format: Optional[str] = None
placeholder_open_graph_description: Optional[str] = None
saved_response: Optional[HttpResponse] = None
tornado_handler: Optional["handlers.AsyncDjangoHandler"] = None
# tornado_handler is a weak reference to work around a memory leak
# in WeakKeyDictionary (https://bugs.python.org/issue44680).
tornado_handler: Optional["weakref.ReferenceType[handlers.AsyncDjangoHandler]"] = None
processed_parameters: Set[str] = field(default_factory=set)
ignored_parameters: Set[str] = field(default_factory=set)

View File

@ -3,6 +3,7 @@ import os
import re
import sys
import time
import weakref
from contextlib import contextmanager
from functools import wraps
from typing import (
@ -310,7 +311,6 @@ class HostRequestMock(HttpRequest):
self.POST[key] = str(post_data[key])
self.method = "POST"
self._tornado_handler = DummyHandler()
self._log_data: Dict[str, Any] = {}
if meta_data is None:
self.META = {"PATH_INFO": "test"}
@ -324,7 +324,7 @@ class HostRequestMock(HttpRequest):
request_notes_map[self] = ZulipRequestNotes(
client_name="",
log_data={},
tornado_handler=tornado_handler,
tornado_handler=None if tornado_handler is None else weakref.ref(tornado_handler),
client=get_client(client_name) if client_name is not None else None,
)

View File

@ -1,5 +1,6 @@
import logging
import urllib
import weakref
from typing import Any, Dict, List
import tornado.web
@ -116,7 +117,7 @@ class AsyncDjangoHandler(tornado.web.RequestHandler, base.BaseHandler):
# Provide a way for application code to access this handler
# given the HttpRequest object.
get_request_notes(request).tornado_handler = self
get_request_notes(request).tornado_handler = weakref.ref(self)
return request

View File

@ -19,7 +19,6 @@ from zerver.lib.validator import (
from zerver.models import Client, UserProfile, get_client, get_user_profile_by_id
from zerver.tornado.event_queue import fetch_events, get_client_descriptor, process_notification
from zerver.tornado.exceptions import BadEventQueueIdError
from zerver.tornado.handlers import AsyncDjangoHandler
@internal_notify_view(True)
@ -111,7 +110,8 @@ def get_events_backend(
# Extract the Tornado handler from the request
tornado_handler = get_request_notes(request).tornado_handler
assert tornado_handler is not None
handler: AsyncDjangoHandler = tornado_handler
handler = tornado_handler()
assert handler is not None
if user_client is None:
valid_user_client = get_request_notes(request).client