Merge lp:~stub/launchpad/librarian-gc into lp:launchpad

Proposed by Stuart Bishop
Status: Merged
Merged at revision: not available
Proposed branch: lp:~stub/launchpad/librarian-gc
Merge into: lp:launchpad
Diff against target: 120 lines
2 files modified
lib/canonical/librarian/ftests/test_gc.py (+53/-29)
lib/canonical/librarian/librariangc.py (+1/-0)
To merge this branch: bzr merge lp:~stub/launchpad/librarian-gc
Reviewer Review Type Date Requested Status
Michael Nelson (community) code Approve
Review via email: mp+12695@code.launchpad.net

This proposal supersedes a proposal from 2009-09-30.

To post a comment you must log in.
Revision history for this message
Stuart Bishop (stub) wrote : Posted in a previous version of this proposal

Per Bug #437084, the librarian garbage collector is still failing on production. This is the fix.

Revision history for this message
Jeroen T. Vermeulen (jtv) wrote : Posted in a previous version of this proposal

Test please.

review: Needs Fixing
Revision history for this message
Stuart Bishop (stub) wrote :

Per Bug #437084, the librarian garbage collector is still failing on production. This is the fix.

Now with test case.

Revision history for this message
Michael Nelson (michael.nelson) wrote :

> Per Bug #437084, the librarian garbage collector is still failing on
> production. This is the fix.
>
> Now with test case.

Thanks stub - nicely tested.

r=me (Teeny style-guide questions below.)

> === modified file 'lib/canonical/librarian/ftests/test_gc.py'
> --- lib/canonical/librarian/ftests/test_gc.py 2009-09-26 09:06:13 +0000
> +++ lib/canonical/librarian/ftests/test_gc.py 2009-10-01 07:09:13 +0000
> @@ -15,6 +15,7 @@
> from pytz import utc
> from sqlobject import SQLObjectNotFound
> from storm.locals import SQL, AutoReload
> +import transaction
> from zope.component import getUtility
>
> from canonical.config import config
> @@ -560,6 +561,21 @@
> self.assert_(os.path.exists(noisefile2_path))
> self.assert_(os.path.exists(noisefile3_path))
>
> + def test_deleteUnwantedFilesBug437084(self):

Just looking at https://dev.launchpad.net/TestsStyleGuide#Python Test Cases
should this be:

       def test_deleteUnwantedFiles_bug437084(self):

or similar?

> + # There was a bug where delete_unwanted_files() would die
> + # if the last file found on disk was unwanted.
> + self.layer.switchDbUser(dbuser='testadmin')
> + content='foo'
> + self.client.addFile(
> + 'foo.txt', len(content), StringIO(content), 'text/plain',
> + )

I always get confused on this one, but I think you've used the tuple
style here, rather than the multiline function call style? That is,
I the closing ')' should be on the previous line and without the extra
comma? See

https://dev.launchpad.net/PythonStyleGuide#Multiline function calls

> + # Roll back the database changes, leaving the file on disk.
> + transaction.abort()

Nice!

> +
> + self.layer.switchDbUser(config.librarian_gc.dbuser)
> +
> + # This should cope.
> + librariangc.delete_unwanted_files(self.con)
>
> def test_cronscript(self):
> script_path = os.path.join(
>
> === modified file 'lib/canonical/librarian/librariangc.py'
> --- lib/canonical/librarian/librariangc.py 2009-09-26 08:43:07 +0000
> +++ lib/canonical/librarian/librariangc.py 2009-10-01 07:09:13 +0000
> @@ -578,6 +578,7 @@
> next_wanted_content_id = get_next_wanted_content_id()
>
> if (config.librarian_server.upstream_host is None
> + and next_wanted_content_id is not None
> and next_wanted_content_id < content_id):
> log.error(
> "LibraryFileContent %d exists in the database but "

And I asked:

<noodles775> stub: hi! Just looking at your MP... would it be worth in addition creating a separate condition to log a warning for this situation where the file exists on disk but not in the db?
<stub> That is actually normal - client.addFile(foo,...); transaction.abort() leaves a file on disk
<noodles775> OK - I assumed that the transaction is only aborted in abnormal circumstances.
<stub> Nah - anytime a web request needs to be retried after uploading a file for instance.
<noodles775> k. great.

--
Michael

review: Approve (code)
Revision history for this message
Stuart Bishop (stub) wrote :

> > + def test_deleteUnwantedFilesBug437084(self):
>
> Just looking at https://dev.launchpad.net/TestsStyleGuide#Python Test Cases
> should this be:
>
> def test_deleteUnwantedFiles_bug437084(self):
>
> or similar?

As per IRC, test_delete_unwanted_files_bug437084().

> > + # There was a bug where delete_unwanted_files() would die
> > + # if the last file found on disk was unwanted.
> > + self.layer.switchDbUser(dbuser='testadmin')
> > + content='foo'
> > + self.client.addFile(
> > + 'foo.txt', len(content), StringIO(content), 'text/plain',
> > + )
>
> I always get confused on this one, but I think you've used the tuple
> style here, rather than the multiline function call style? That is,
> I the closing ')' should be on the previous line and without the extra
> comma? See
>
> https://dev.launchpad.net/PythonStyleGuide#Multiline function calls

Yup. Copy and paste glitch. Fixed.

Thanks :)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/canonical/librarian/ftests/test_gc.py'
2--- lib/canonical/librarian/ftests/test_gc.py 2009-09-26 09:06:13 +0000
3+++ lib/canonical/librarian/ftests/test_gc.py 2009-10-01 08:05:25 +0000
4@@ -5,6 +5,7 @@
5
6 __metaclass__ = type
7
8+import shutil
9 import sys
10 import os
11 from subprocess import Popen, PIPE, STDOUT
12@@ -15,6 +16,7 @@
13 from pytz import utc
14 from sqlobject import SQLObjectNotFound
15 from storm.locals import SQL, AutoReload
16+import transaction
17 from zope.component import getUtility
18
19 from canonical.config import config
20@@ -529,37 +531,59 @@
21 # Long non-hexadecimal number
22 noisedir3_path = os.path.join(config.librarian_server.root, '11.bak')
23
24- os.mkdir(noisedir1_path)
25- os.mkdir(noisedir2_path)
26- os.mkdir(noisedir3_path)
27-
28- # Files in the noise directories.
29- noisefile1_path = os.path.join(noisedir1_path, 'abc')
30- noisefile2_path = os.path.join(noisedir2_path, 'def')
31- noisefile3_path = os.path.join(noisedir2_path, 'ghi')
32- open(noisefile1_path, 'w').write('hello')
33- open(noisefile2_path, 'w').write('there')
34- open(noisefile3_path, 'w').write('testsuite')
35-
36- # Pretend it is tomorrow to ensure the files don't count as
37- # recently created, and run the delete_unwanted_files process.
38- org_time = librariangc.time
39- def tomorrow_time():
40- return org_time() + 24 * 60 * 60 + 1
41 try:
42- librariangc.time = tomorrow_time
43- librariangc.delete_unwanted_files(self.con)
44+ os.mkdir(noisedir1_path)
45+ os.mkdir(noisedir2_path)
46+ os.mkdir(noisedir3_path)
47+
48+ # Files in the noise directories.
49+ noisefile1_path = os.path.join(noisedir1_path, 'abc')
50+ noisefile2_path = os.path.join(noisedir2_path, 'def')
51+ noisefile3_path = os.path.join(noisedir2_path, 'ghi')
52+ open(noisefile1_path, 'w').write('hello')
53+ open(noisefile2_path, 'w').write('there')
54+ open(noisefile3_path, 'w').write('testsuite')
55+
56+ # Pretend it is tomorrow to ensure the files don't count as
57+ # recently created, and run the delete_unwanted_files process.
58+ org_time = librariangc.time
59+ def tomorrow_time():
60+ return org_time() + 24 * 60 * 60 + 1
61+ try:
62+ librariangc.time = tomorrow_time
63+ librariangc.delete_unwanted_files(self.con)
64+ finally:
65+ librariangc.time = org_time
66+
67+ # None of the rubbish we created has been touched.
68+ self.assert_(os.path.isdir(noisedir1_path))
69+ self.assert_(os.path.isdir(noisedir2_path))
70+ self.assert_(os.path.isdir(noisedir3_path))
71+ self.assert_(os.path.exists(noisefile1_path))
72+ self.assert_(os.path.exists(noisefile2_path))
73+ self.assert_(os.path.exists(noisefile3_path))
74 finally:
75- librariangc.time = org_time
76-
77- # None of the rubbish we created has been touched.
78- self.assert_(os.path.isdir(noisedir1_path))
79- self.assert_(os.path.isdir(noisedir2_path))
80- self.assert_(os.path.isdir(noisedir3_path))
81- self.assert_(os.path.exists(noisefile1_path))
82- self.assert_(os.path.exists(noisefile2_path))
83- self.assert_(os.path.exists(noisefile3_path))
84-
85+ # We need to clean this up ourselves, as the standard librarian
86+ # cleanup only removes files it knows where valid to avoid
87+ # accidents.
88+ shutil.rmtree(noisedir1_path)
89+ shutil.rmtree(noisedir2_path)
90+ shutil.rmtree(noisedir3_path)
91+
92+ def test_delete_unwanted_files_bug437084(self):
93+ # There was a bug where delete_unwanted_files() would die
94+ # if the last file found on disk was unwanted.
95+ self.layer.switchDbUser(dbuser='testadmin')
96+ content='foo'
97+ self.client.addFile(
98+ 'foo.txt', len(content), StringIO(content), 'text/plain')
99+ # Roll back the database changes, leaving the file on disk.
100+ transaction.abort()
101+
102+ self.layer.switchDbUser(config.librarian_gc.dbuser)
103+
104+ # This should cope.
105+ librariangc.delete_unwanted_files(self.con)
106
107 def test_cronscript(self):
108 script_path = os.path.join(
109
110=== modified file 'lib/canonical/librarian/librariangc.py'
111--- lib/canonical/librarian/librariangc.py 2009-09-26 08:43:07 +0000
112+++ lib/canonical/librarian/librariangc.py 2009-10-01 08:05:25 +0000
113@@ -578,6 +578,7 @@
114 next_wanted_content_id = get_next_wanted_content_id()
115
116 if (config.librarian_server.upstream_host is None
117+ and next_wanted_content_id is not None
118 and next_wanted_content_id < content_id):
119 log.error(
120 "LibraryFileContent %d exists in the database but "