Merge lp:~cjwatson/launchpad/dep11-mtime into lp:launchpad

Proposed by Colin Watson
Status: Merged
Merged at revision: 17946
Proposed branch: lp:~cjwatson/launchpad/dep11-mtime
Merge into: lp:launchpad
Diff against target: 138 lines (+45/-15)
2 files modified
lib/lp/archivepublisher/publishing.py (+21/-11)
lib/lp/archivepublisher/tests/test_publisher.py (+24/-4)
To merge this branch: bzr merge lp:~cjwatson/launchpad/dep11-mtime
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+288757@code.launchpad.net

Commit message

Fine-tune publisher timestamp syncing to avoid interfering with files that are fetched from other sources rather than generated internally.

Description of the change

Fine-tune publisher timestamp syncing to avoid interfering with files that are fetched from other sources rather than generated internally.

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) wrote :

This will fix the out of dateness problem, but will it not reintroduce the timestamp-based cache header desync thing for non-core files?

review: Approve (code)
Revision history for this message
Colin Watson (cjwatson) wrote :

The relevant cache headers are only set for a very limited set of files.
E.g. in ubuntu-mirror:

    <Files ~ "Release(\.gpg)?|InRelease|Packages\.(xz|bz2|gz)|Sources\.(xz|bz2|gz)?|Translation-.*?$">
        ExpiresActive On
        ExpiresDefault "modification plus 1800 seconds"
        Header append Cache-Control "proxy-revalidate"
    </Files>

In general this seems to equate to the core set of files produced by
apt-ftparchive and other necessary publisher functions, rather than the
ones I'm calling "extra" files that are synced in from elsewhere; the
latter would never have had special Expires applied, so all we were
achieving was to bump its Last-Modified, which probably didn't achieve
much except causing unnecessary redownloads. curl against the current
archive confirms:

  $ curl -Is http://archive.ubuntu.com/ubuntu/dists/xenial/InRelease | egrep '^(Cache-Control|Expires):'
  Cache-Control: max-age=1264, proxy-revalidate
  Expires: Tue, 15 Mar 2016 02:48:00 GMT
  $ curl -Is http://archive.ubuntu.com/ubuntu/dists/xenial/main/dep11/Components-amd64.yml.gz | egrep '^(Cache-Control|Expires):'
  $

Of course this is all fairly weird and ought to be replaced by by-hash,
but in the interim I don't think this change breaks anything to do with
cache headers.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/archivepublisher/publishing.py'
2--- lib/lp/archivepublisher/publishing.py 2016-02-12 01:29:49 +0000
3+++ lib/lp/archivepublisher/publishing.py 2016-03-11 12:14:23 +0000
4@@ -838,15 +838,24 @@
5 self.archive.getComponentsForSeries(distroseries)]
6 all_architectures = [
7 a.architecturetag for a in distroseries.enabled_architectures]
8- all_files = set()
9+ # Core files are those that are normally updated when a suite
10+ # changes, and which therefore receive special treatment with
11+ # caching headers on mirrors.
12+ core_files = set()
13+ # Extra files are updated occasionally from other sources. They are
14+ # still checksummed and indexed, but they do not receive special
15+ # treatment with caching headers on mirrors. We must not play any
16+ # special games with timestamps here, as it will interfere with the
17+ # "staging" mechanism used to update these files.
18+ extra_files = set()
19 for component in all_components:
20 self._writeSuiteSource(
21- distroseries, pocket, component, all_files)
22+ distroseries, pocket, component, core_files)
23 for architecture in all_architectures:
24 self._writeSuiteArch(
25- distroseries, pocket, component, architecture, all_files)
26+ distroseries, pocket, component, architecture, core_files)
27 self._writeSuiteI18n(
28- distroseries, pocket, component, all_files)
29+ distroseries, pocket, component, core_files)
30 dep11_dir = os.path.join(
31 self._config.distsroot, suite, component, "dep11")
32 try:
33@@ -855,15 +864,16 @@
34 dep11_file.startswith("icons-")):
35 dep11_path = os.path.join(
36 component, "dep11", dep11_file)
37- all_files.add(remove_suffix(dep11_path))
38- all_files.add(dep11_path)
39+ extra_files.add(remove_suffix(dep11_path))
40+ extra_files.add(dep11_path)
41 except OSError as e:
42 if e.errno != errno.ENOENT:
43 raise
44 for architecture in all_architectures:
45 for contents_path in get_suffixed_indices(
46 'Contents-' + architecture):
47- all_files.add(contents_path)
48+ extra_files.add(contents_path)
49+ all_files = core_files | extra_files
50
51 drsummary = "%s %s " % (self.distro.displayname,
52 distroseries.displayname)
53@@ -898,20 +908,20 @@
54 release_file.setdefault("SHA256", []).append(hashes["sha256"])
55
56 self._writeReleaseFile(suite, release_file)
57- all_files.add("Release")
58+ core_files.add("Release")
59
60 if self.archive.signing_key is not None:
61 # Sign the repository.
62 IArchiveSigningKey(self.archive).signRepository(suite)
63- all_files.add("Release.gpg")
64- all_files.add("InRelease")
65+ core_files.add("Release.gpg")
66+ core_files.add("InRelease")
67 else:
68 # Skip signature if the archive signing key is undefined.
69 self.log.debug("No signing key available, skipping signature.")
70
71 # Make sure all the timestamps match, to make it easier to insert
72 # caching headers on mirrors.
73- self._syncTimestamps(suite, all_files)
74+ self._syncTimestamps(suite, core_files)
75
76 def _writeSuiteArchOrSource(self, distroseries, pocket, component,
77 file_stub, arch_name, arch_path,
78
79=== modified file 'lib/lp/archivepublisher/tests/test_publisher.py'
80--- lib/lp/archivepublisher/tests/test_publisher.py 2016-02-12 01:29:49 +0000
81+++ lib/lp/archivepublisher/tests/test_publisher.py 2016-03-11 12:14:23 +0000
82@@ -22,7 +22,10 @@
83 import lzma
84 except ImportError:
85 from backports import lzma
86-from testtools.matchers import ContainsAll
87+from testtools.matchers import (
88+ ContainsAll,
89+ LessThan,
90+ )
91 import transaction
92 from zope.component import getUtility
93 from zope.security.proxy import removeSecurityProxy
94@@ -1863,7 +1866,7 @@
95 release, os.path.join('main', 'dep11', name), f.read())
96
97 def testReleaseFileTimestamps(self):
98- # The timestamps of Release and all its entries match.
99+ # The timestamps of Release and all its core entries match.
100 publisher = Publisher(
101 self.logger, self.config, self.disk_pool,
102 self.ubuntutest.main_archive)
103@@ -1878,16 +1881,33 @@
104 sources = suite_path('main', 'source', 'Sources.gz')
105 sources_timestamp = os.stat(sources).st_mtime - 60
106 os.utime(sources, (sources_timestamp, sources_timestamp))
107+ dep11_path = suite_path('main', 'dep11')
108+ dep11_names = ('Components-amd64.yml.gz', 'Components-i386.yml.gz',
109+ 'icons-64x64.tar.gz', 'icons-128x128.tar.gz')
110+ os.makedirs(dep11_path)
111+ now = time.time()
112+ for name in dep11_names:
113+ with gzip.GzipFile(os.path.join(dep11_path, name), 'wb') as f:
114+ f.write(name)
115+ os.utime(os.path.join(dep11_path, name), (now - 60, now - 60))
116
117 publisher.D_writeReleaseFiles(False)
118
119 release = self.parseRelease(suite_path('Release'))
120 paths = ['Release'] + [entry['name'] for entry in release['md5sum']]
121 timestamps = set(
122- os.stat(suite_path(path)).st_mtime
123- for path in paths if os.path.exists(suite_path(path)))
124+ os.stat(suite_path(path)).st_mtime for path in paths
125+ if '/dep11/' not in path and os.path.exists(suite_path(path)))
126 self.assertEqual(1, len(timestamps))
127
128+ # Non-core files preserve their original timestamps.
129+ # (Due to https://bugs.python.org/issue12904, there is some loss of
130+ # accuracy in the test.)
131+ for name in dep11_names:
132+ self.assertThat(
133+ os.stat(os.path.join(dep11_path, name)).st_mtime,
134+ LessThan(now - 59))
135+
136 def testCreateSeriesAliasesNoAlias(self):
137 """createSeriesAliases has nothing to do by default."""
138 publisher = Publisher(