Merge ~cjwatson/launchpad:pool-file-overwrite-error-enormous-oopses into launchpad:master

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: 47e9a7e9a1717ba2f40e9fe3e01924d7418b46da
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~cjwatson/launchpad:pool-file-overwrite-error-enormous-oopses
Merge into: launchpad:master
Diff against target: 105 lines (+41/-2)
4 files modified
lib/lp/services/timeline/requesttimeline.py (+20/-0)
lib/lp/services/timeline/tests/test_requesttimeline.py (+14/-0)
lib/lp/soyuz/model/publishing.py (+3/-1)
lib/lp/soyuz/tests/test_publishing.py (+4/-1)
Reviewer Review Type Date Requested Status
Guruprasad Approve
Review via email: mp+436331@code.launchpad.net

Commit message

Suppress timeline in PoolFileOverwriteError OOPSes

Description of the change

The traceback for `PoolFileOverwriteError` (raised when the publisher attempts to overwrite an existing file in a pool with different content) tells us everything we need to know: we get both of the checksums and the target path.

However, the OOPSes also contain an enormous timeline mainly consisting of every SQL statement the publisher has issued as part of processing that archive. These timelines tend to cause the OOPS processing system to back up, and provide essentially no information of any value. Instead, just arrange for these OOPSes to be raised with a temporary empty timeline.

To post a comment you must log in.
Revision history for this message
Guruprasad (lgp171188) wrote :

LGTM 👍

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/timeline/requesttimeline.py b/lib/lp/services/timeline/requesttimeline.py
2index 0c1460c..7fb0332 100644
3--- a/lib/lp/services/timeline/requesttimeline.py
4+++ b/lib/lp/services/timeline/requesttimeline.py
5@@ -6,8 +6,11 @@
6 __all__ = [
7 "get_request_timeline",
8 "set_request_timeline",
9+ "temporary_request_timeline",
10 ]
11
12+from contextlib import contextmanager
13+
14 from timeline import Timeline
15
16 # XXX RobertCollins 2010-09-01 bug=623199: Undesirable but pragmatic.
17@@ -48,3 +51,20 @@ def set_request_timeline(request, timeline):
18 return timeline
19 # Disabled code path: bug 623199, ideally we would use this code path.
20 request.annotations["timeline"] = timeline
21+
22+
23+@contextmanager
24+def temporary_request_timeline(request):
25+ """Give `request` a temporary timeline.
26+
27+ This is useful in contexts where we want to raise an OOPS but we know
28+ that the timeline is uninteresting and may be very large.
29+
30+ :param request: A Zope/Launchpad request object.
31+ """
32+ old_timeline = get_request_timeline(request)
33+ try:
34+ set_request_timeline(request, Timeline())
35+ yield
36+ finally:
37+ set_request_timeline(request, old_timeline)
38diff --git a/lib/lp/services/timeline/tests/test_requesttimeline.py b/lib/lp/services/timeline/tests/test_requesttimeline.py
39index 341ce37..a49ba8a 100644
40--- a/lib/lp/services/timeline/tests/test_requesttimeline.py
41+++ b/lib/lp/services/timeline/tests/test_requesttimeline.py
42@@ -11,6 +11,7 @@ from lp.services import webapp
43 from lp.services.timeline.requesttimeline import (
44 get_request_timeline,
45 set_request_timeline,
46+ temporary_request_timeline,
47 )
48
49
50@@ -55,3 +56,16 @@ class TestRequestTimeline(testtools.TestCase):
51 self.assertEqual(timeline, get_request_timeline(req))
52
53 # Tests while adapter._local contains the timeline ---end---
54+
55+ def test_temporary_request_timeline(self):
56+ req = TestRequest()
57+ timeline = Timeline()
58+ set_request_timeline(req, timeline)
59+ timeline.start("test", "test").finish()
60+ self.assertEqual(timeline, get_request_timeline(req))
61+ self.assertEqual(1, len(timeline.actions))
62+ with temporary_request_timeline(req):
63+ self.assertNotEqual(timeline, get_request_timeline(req))
64+ get_request_timeline(req).start("test-2", "test-2").finish()
65+ self.assertEqual(timeline, get_request_timeline(req))
66+ self.assertEqual(1, len(timeline.actions))
67diff --git a/lib/lp/soyuz/model/publishing.py b/lib/lp/soyuz/model/publishing.py
68index b2f7a9d..82eea43 100644
69--- a/lib/lp/soyuz/model/publishing.py
70+++ b/lib/lp/soyuz/model/publishing.py
71@@ -50,6 +50,7 @@ from lp.services.database.stormexpr import IsDistinctFrom
72 from lp.services.librarian.browser import ProxiedLibraryFileAlias
73 from lp.services.librarian.model import LibraryFileAlias, LibraryFileContent
74 from lp.services.propertycache import cachedproperty, get_property_cache
75+from lp.services.timeline.requesttimeline import temporary_request_timeline
76 from lp.services.webapp.errorlog import ErrorReportingUtility, ScriptRequest
77 from lp.services.worlddata.model.country import Country
78 from lp.soyuz.adapters.proxiedsourcefiles import ProxiedSourceLibraryFileAlias
79@@ -190,7 +191,8 @@ class ArchivePublisherBase:
80 properties = [("error-explanation", message)]
81 request = ScriptRequest(properties)
82 error_utility = ErrorReportingUtility()
83- error_utility.raising(sys.exc_info(), request)
84+ with temporary_request_timeline(request):
85+ error_utility.raising(sys.exc_info(), request)
86 log.error("%s (%s)" % (message, request.oopsid))
87 else:
88 self.setPublished()
89diff --git a/lib/lp/soyuz/tests/test_publishing.py b/lib/lp/soyuz/tests/test_publishing.py
90index d2e041c..7812578 100644
91--- a/lib/lp/soyuz/tests/test_publishing.py
92+++ b/lib/lp/soyuz/tests/test_publishing.py
93@@ -994,8 +994,11 @@ class TestNativePublishing(TestNativePublishingBase):
94 pub_source = self.getPubSource(filecontent=b"Something")
95 pub_source.publish(self.disk_pool, self.logger)
96
97- # And an oops should be filed for the error.
98+ # An oops should be filed for the error, but we don't include the
99+ # SQL timeline; it may be very large and tells us nothing that we
100+ # can't get from the error message.
101 self.assertEqual("PoolFileOverwriteError", self.oopses[0]["type"])
102+ self.assertEqual([], self.oopses[0]["timeline"])
103
104 self.layer.commit()
105 self.assertEqual(pub_source.status, PackagePublishingStatus.PENDING)

Subscribers

People subscribed via source and target branches

to status/vote changes: