Merge lp:~cjwatson/launchpad/contents-nanoseconds into lp:launchpad

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: no longer in the source branch.
Merged at revision: 17410
Proposed branch: lp:~cjwatson/launchpad/contents-nanoseconds
Merge into: lp:launchpad
Diff against target: 63 lines (+35/-0)
2 files modified
lib/lp/archivepublisher/scripts/publish_ftpmaster.py (+9/-0)
lib/lp/archivepublisher/tests/test_publish_ftpmaster.py (+26/-0)
To merge this branch: bzr merge lp:~cjwatson/launchpad/contents-nanoseconds
Reviewer Review Type Date Requested Status
Colin Watson (community) Approve
Review via email: mp+253678@code.launchpad.net

Commit message

Work around Python bug that loses nanosecond precision in Contents file timestamps, causing them to be re-updated every time.

Description of the change

The saga continues ...

I just noticed that the publisher is updating lots of Contents files on every run, which on closer investigation turns out to be because Python's os.utime truncates the timestamp to microsecond resolution, causing the copied file to appear older (fixed in Python 3.3, but we won't be on that for quite some time). This works around that bug by rounding the timestamp up to the nearest second.

To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/archivepublisher/scripts/publish_ftpmaster.py'
2--- lib/lp/archivepublisher/scripts/publish_ftpmaster.py 2015-03-20 11:36:49 +0000
3+++ lib/lp/archivepublisher/scripts/publish_ftpmaster.py 2015-03-20 14:09:39 +0000
4@@ -9,6 +9,7 @@
5 ]
6
7 from datetime import datetime
8+import math
9 import os
10 import shutil
11
12@@ -579,6 +580,14 @@
13 "Installing new Contents file for %s/%s.", suite,
14 arch.architecturetag)
15 shutil.copy2(new_contents, current_contents)
16+ # Due to http://bugs.python.org/issue12904, shutil.copy2 doesn't
17+ # copy timestamps precisely, and unfortunately it rounds down.
18+ # If we must lose accuracy, we need to round up instead. This
19+ # can be removed once Launchpad runs on Python >= 3.3.
20+ st = os.stat(new_contents)
21+ os.utime(
22+ current_contents,
23+ (math.ceil(st.st_atime), math.ceil(st.st_mtime)))
24 return True
25 return False
26
27
28=== modified file 'lib/lp/archivepublisher/tests/test_publish_ftpmaster.py'
29--- lib/lp/archivepublisher/tests/test_publish_ftpmaster.py 2015-03-20 11:36:49 +0000
30+++ lib/lp/archivepublisher/tests/test_publish_ftpmaster.py 2015-03-20 14:09:39 +0000
31@@ -868,6 +868,32 @@
32 "Contents",
33 read_marker_file([backup_suite, "%s.gz" % contents_filename]))
34
35+ def test_updateContentsFile_twice(self):
36+ # If updateContentsFile is run twice in a row, it does not update
37+ # the file the second time.
38+ distro = self.makeDistroWithPublishDirectory()
39+ distroseries = self.factory.makeDistroSeries(distribution=distro)
40+ das = self.factory.makeDistroArchSeries(distroseries=distroseries)
41+ script = self.makeScript(distro)
42+ script.setUp()
43+ script.setUpDirs()
44+ archive_config = getPubConfig(distro.main_archive)
45+ contents_filename = "Contents-%s" % das.architecturetag
46+ backup_suite = os.path.join(
47+ archive_config.archiveroot + "-distscopy", "dists",
48+ distroseries.name)
49+ os.makedirs(backup_suite)
50+ content_suite = os.path.join(
51+ archive_config.distroroot, "contents-generation", distro.name,
52+ "dists", distroseries.name)
53+ os.makedirs(content_suite)
54+ write_marker_file(
55+ [content_suite, "%s-staged.gz" % contents_filename], "Contents")
56+ self.assertTrue(script.updateContentsFile(
57+ distro.main_archive, distro, distroseries.name, das))
58+ self.assertFalse(script.updateContentsFile(
59+ distro.main_archive, distro, distroseries.name, das))
60+
61 def test_updateContentsFiles_updated_suites(self):
62 # updateContentsFiles returns a list of suites for which it updated
63 # Contents files.