Merge ~cjwatson/launchpad:publication-discard-previous-interaction into launchpad:master

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: 616079bd16d7484e48254b97564867b67a608c65
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~cjwatson/launchpad:publication-discard-previous-interaction
Merge into: launchpad:master
Diff against target: 72 lines (+36/-1)
2 files modified
lib/lp/services/webapp/publication.py (+13/-1)
lib/lp/services/webapp/tests/test_publication.py (+23/-0)
Reviewer Review Type Date Requested Status
Tom Wardill (community) Approve
Review via email: mp+403193@code.launchpad.net

Commit message

Discard previous interaction in endRequest

Description of the change

Zope's `BrowserPublication.endRequest` leaves a reference to the previous interaction lying around in thread-local storage just in case somebody might want to call `restoreInteraction` later. However, this significantly complicates cyclic garbage collection and memory leak analysis, because it means that objects referenced by a request can never be garbage-collected immediately after the request finishes, but at best only somewhat later.

There are cases in Launchpad where we do use `restoreInteraction`, but none of them are after the end of publishing a request, so simplify matters by discarding the previous interaction.

To post a comment you must log in.
Revision history for this message
Tom Wardill (twom) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/lib/lp/services/webapp/publication.py b/lib/lp/services/webapp/publication.py
2index 8517e7f..b0dabea 100644
3--- a/lib/lp/services/webapp/publication.py
4+++ b/lib/lp/services/webapp/publication.py
5@@ -52,7 +52,10 @@ from zope.publisher.interfaces.browser import (
6 IDefaultSkin,
7 )
8 from zope.publisher.publish import mapply
9-from zope.security.management import newInteraction
10+from zope.security.management import (
11+ endInteraction,
12+ newInteraction,
13+ )
14 from zope.security.proxy import (
15 isinstance as zope_isinstance,
16 removeSecurityProxy,
17@@ -785,6 +788,15 @@ class LaunchpadBrowserPublication(
18 def endRequest(self, request, object):
19 superclass = zope.app.publication.browser.BrowserPublication
20 superclass.endRequest(self, request, object)
21+ # BrowserPublication.endRequest calls endInteraction as well, but
22+ # calling it once leaves a reference to the previous interaction
23+ # around in
24+ # zope.security.management.thread_local.previous_interaction just in
25+ # case somebody might want to call restoreInteraction later,
26+ # significantly complicating memory leak analysis. We won't need to
27+ # restore the previous interaction in this case, so call
28+ # endInteraction again to discard it.
29+ endInteraction()
30
31 da.clear_request_started()
32
33diff --git a/lib/lp/services/webapp/tests/test_publication.py b/lib/lp/services/webapp/tests/test_publication.py
34index 97d67ad..0f96bb5 100644
35--- a/lib/lp/services/webapp/tests/test_publication.py
36+++ b/lib/lp/services/webapp/tests/test_publication.py
37@@ -25,6 +25,10 @@ from zope.publisher.interfaces import (
38 NotFound,
39 Retry,
40 )
41+from zope.publisher.publish import publish
42+from zope.security.management import (
43+ thread_local as zope_security_thread_local,
44+ )
45
46 from lp.services.database.interfaces import IMasterStore
47 from lp.services.identity.model.emailaddress import EmailAddress
48@@ -85,6 +89,25 @@ class TestLaunchpadBrowserPublication(TestCase):
49 self.assertEqual(request.traversed_objects, [obj1])
50
51
52+class TestLaunchpadBrowserPublicationInteractionHandling(TestCase):
53+
54+ layer = DatabaseFunctionalLayer
55+
56+ def test_endRequest_removes_previous_interaction(self):
57+ # Zope's BrowserPublication.endRequest leaves a reference to the
58+ # previous interaction around in
59+ # zope.security.management.thread_local.previous_interaction, which
60+ # can complicate memory leak analysis. Since we don't need this
61+ # reference, LaunchpadBrowserPublication.endRequest removes it.
62+ request = LaunchpadTestRequest(PATH_INFO='/')
63+ request.setPublication(LaunchpadBrowserPublication(None))
64+ publish(request)
65+ self.assertIsNone(
66+ getattr(zope_security_thread_local, 'interaction', None))
67+ self.assertIsNone(
68+ getattr(zope_security_thread_local, 'previous_interaction', None))
69+
70+
71 class TestWebServicePublication(TestCaseWithFactory):
72 layer = DatabaseFunctionalLayer
73

Subscribers

People subscribed via source and target branches

to status/vote changes: