Skip to content

Commit

Permalink
ansible/hg-web: serve static files as immutable content; r=sheehan
Browse files Browse the repository at this point in the history
The commit message of the previous commit documented a problem with
naive static file serving using httpd. In summary, Last-Modified
and ETag values vary depending on the filesystem and across N servers
this can result in different header values and caching state. This can
result in lower cache hit rates.

We can do better.

Since the static files are checked into v-c-t, any particular revision
of v-c-t has immutable state of those files. In addition, the HTTP
Cache-Control header can be used to tell clients to always use a
cached result without lookup (max-age) and that content is immutable
(immutable).

Putting these two properties together, it means that we can serve up
static files from URLs with the v-c-t revision in them with very
aggressive caching headers. This should result in very high cache
hit rates coupled with the removal of conditional HTTP requests. What
this means is that most hgweb page loads will require 1 roundtrip to
the server (to fetch the main HTML) and static assets will load
immediately.

Differential Revision: https://phabricator.services.mozilla.com/D10786

--HG--
extra : moz-landing-system : lando
  • Loading branch information
indygreg committed Nov 5, 2018
1 parent a8c461c commit d1678de
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 10 deletions.
26 changes: 26 additions & 0 deletions ansible/roles/hg-web/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,32 @@
- name: synchronize hg templates
command: /usr/bin/rsync -a --delete-after /var/hg/version-control-tools/hgtemplates/ /repo/hg/hg_templates/

- name: synchronize hg templates
command: /usr/bin/rsync -a --delete-after /var/hg/version-control-tools/hgtemplates/ /repo/hg/hg_templates/

- name: directory for hgweb static files
file: path=/repo/hg/htdocs/static
state=directory
owner=hg
group=hg
mode=0775

# We need to do this before hgrc is written, as hgrc references the per-revision
# directory.
- name: synchronize static files to versioned directory
command: /usr/bin/rsync -a /repo/hg/hg_templates/static/ /repo/hg/htdocs/static/{{ vct_node | mandatory }}/

- name: symlink to latest static files
file: src=/repo/hg/htdocs/static/{{ vct_node | mandatory }}
dest=/repo/hg/htdocs/static/latest
state=link
owner=hg
group=hg

# TODO we orphan "old" revisions of static content. We could potentially
# purge old revisions. But since the size of static content is small and there
# is little harm to keeping the files around, it doesn't seem justified.

# Settings from this file are inherited by every hg command run on the
# system.
- name: install global hgrc
Expand Down
2 changes: 1 addition & 1 deletion ansible/roles/hg-web/templates/hgrc.j2
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ allow_archive = bz2 gz zip
templates = /repo_local/mozilla/hg_templates/
encoding = UTF-8
baseurl = https://hg.mozilla.org/
staticurl = /static/
staticurl = /static/{{ vct_node | mandatory }}/
maxchanges = 20
guessmime = True

Expand Down
15 changes: 11 additions & 4 deletions ansible/roles/hg-web/templates/vhost.conf.j2
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,21 @@ LimitRequestFields 1000
{% endfor %}
</Location>

# Serve static files from templates directory straight from disk.
<Directory /repo/hg/hg_templates/static/>
Options None
# Serve static files straight from disk.
<Directory /repo/hg/htdocs/static/>
Options FollowSymLinks
AllowOverride None
Require all granted
</Directory>

Alias /static/ /repo/hg/hg_templates/static/
Alias /static/ /repo/hg/htdocs/static/

# Serve static files with custom cache rules. Since on-disk files
# and URLs are versioned by the v-c-t revision, they are immutable
# and can be served with aggressive caching settings.
<Location /static/>
Header set Cache-Control "max-age=31536000, immutable"
</Location>

#LogLevel debug
LogFormat "%h %v %u %t \"%r\" %>s %b %D \"%{Referer}i\" \"%{User-Agent}i\" \"%{Cookie}i\""
Expand Down
29 changes: 24 additions & 5 deletions hgserver/tests/test-static-files.t
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,28 @@

/static/ serves hgtemplates static files

$ http ${HGWEB_0_URL}static/style.css --body-file body
$ http ${HGWEB_0_URL}static/latest/style.css --body-file body
200
accept-ranges: bytes
cache-control: max-age=31536000, immutable
connection: close
content-length: * (glob)
content-type: text/css
date: * (glob)
etag: * (glob)
last-modified: * (glob)
server: Apache
strict-transport-security: max-age=31536000
vary: Accept-Encoding
x-content-type-options: nosniff

$ head -n 1 body
a { text-decoration:none; }

$ http ${HGWEB_0_URL}static/DOCKER/style.css --body-file body
200
accept-ranges: bytes
cache-control: max-age=31536000, immutable
connection: close
content-length: * (glob)
content-type: text/css
Expand All @@ -30,9 +49,9 @@
Repositories reference /static/ for static URLs

$ http ${HGWEB_0_URL}mozilla-central | grep static
<link rel="icon" href="/static/hgicon.png" type="image/png" />
<link rel="stylesheet" href="/static/style-gitweb.css" type="text/css" />
<script type="text/javascript" src="/static/mercurial.js"></script>
<img src="/static/moz-logo-bw-rgb.svg" alt="mercurial" />
<link rel="icon" href="/static/DOCKER/hgicon.png" type="image/png" />
<link rel="stylesheet" href="/static/DOCKER/style-gitweb.css" type="text/css" />
<script type="text/javascript" src="/static/DOCKER/mercurial.js"></script>
<img src="/static/DOCKER/moz-logo-bw-rgb.svg" alt="mercurial" />

$ hgmo clean

0 comments on commit d1678de

Please sign in to comment.