Exploring methods to purge Varnish cache

I’m considering implementing Varnish at Stanford Law and have been playing with it here to understand implications and workout issues. One issue is purging pages from cache as they are modified or new pages are added. Varnish provides several methods to do this (varnishadm, telnet and http).

1. Varnishadm

Purge a specific page (front page of this blog, for example):

varnishadm -T 127.0.0.1:6082 purge req.url == "/alain/"

Result:

varnishadm -T 127.0.0.1:6082 purge.list
    0 	req.url == /alain/

Purge any page matching a regular expression (front page of this blog, for example):

varnishadm -T 127.0.0.1:6082 purge.url "^/alain/$"

Result:

varnishadm -T 127.0.0.1:6082 purge.list
    0 	req.url ~ ^/alain/$

2. Telnet

Similar to running varnishadm, except we have to open a telnet connection first:

telnet localhost 6082
purge req.url == "/alain/"
200 0

My boxes don’t accept telnet connections from the Internets so I can’t think of a scenario where I’d be talking to varnishd via telnet, so varnishadm is what I’d use at the shell or in a script.

3. HTTP

This one is probably the one that will see the most use as it is most accessible for a web based application. The only caveat is that it doesn’t support regular expressions, so just one URL at a time can be passed this way.

Will test first via telnet:

Request:
telnet localhost 80
 
Response:
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
 
Request:
PURGE /alain/ HTTP/1.0
Host: giantdorks.org
 
Response:
HTTP/1.0 200 OK
Date: Tue, 11 May 2010 02:00:00 GMT
Server: Apache
X-Pingback: http://giantdorks.org/alain/xmlrpc.php
Vary: Accept-Encoding
Connection: close
Content-Type: text/html; charset=UTF-8
 
...
Followed by the HTML output of the page

Note that the HTTP request was “PURGE HTTP/1.0″ instead of the usual “GET HTTP/1.1″.

Now on to a more useful example. Here’s a PHP example using the built in fsockopen function, example based on the one offered at php.net:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
$fp = fsockopen("127.0.0.1", "80", $errno, $errstr, 2);
if (!$fp) {
    echo "$errstr ($errno)<br />\n";
} else {
    $out = "PURGE /alain HTTP/1.0\r\n";
    $out .= "Host: giantdorks.org\r\n";
    $out .= "Connection: Close\r\n\r\n";
    fwrite($fp, $out);
    while (!feof($fp)) {
        echo fgets($fp, 128);
    }
    fclose($fp);
}
?>

Here’s another one. Presents user with a form to supply the URL and host (virtual host). Slightly modified example pulled from varnish-cache.org:

index.html:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
 
<html>
<head>
        <title>Purge Varnish cache</title>
</head>
 
<style type="text/css">
  body {
        font-size: 10px;
  }
  h1 {
        font-weight: bold;
        color: #000000;
        border-bottom: 1px solid #C6EC8C;
        margin-bottom: 2em;
  }
  label {
        font-size: 160%;
        float: left;
        text-align: right;
        margin-right: 0.5em;
        display: block
  }
  input[type="text"] {
        width: 500px;
  }
  .submit input {
        margin-left: 0em;
        margin-bottom: 1em;
  }
</style>
 
<body>
 
  <h1>Makes Varnish purge the supplied URL from its cache</h1>
 
  <form action="vpurge.php" method="post">
        <p><label>URL</label> <input type="text" name="url"></p>
        <p><label>HOST</label> <input type="text" name="host"></p>
        <p class="submit"><input value="Submit" type="submit"></p>
  </form>
 
</body>
</html>

vpurge.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?php
# get param
$url = $_POST["url"];
$host = $_POST["host"];
 
  $ip = "127.0.0.1";
  $port = "80";
 
  $timeout = 1;
  $verbose = 1;
 
  # inits
  $sock = fsockopen ($ip,$port,$errno, $errstr,$timeout);
  if (!$sock) { echo "connections failed $errno $errstr"; exit; }
 
  if ( !($url || $host) ) { echo "No params"; exit; }
 
  stream_set_timeout($sock,$timeout);
 
  $pcommand = "purge";
  # Send command
  $pcommand .= ".hash $url#$host#";
 
  put ($pcommand);
  put ("quit");
 
  fclose ($sock);
 
  function readit() {
    global $sock,$verbose;
    if (!$verbose) { return; }
    while ($sockstr = fgets($sock,1024)) {
      $str .= "rcv: " . $sockstr . "<br>";
    }
    if ($verbose) { echo "$str\n"; }
  }
 
  function put($str) {
    global $sock,$verbose;
    fwrite ($sock, $str . "\r\n");
    if ($verbose) { echo "send: $str <br>\n"; }
    readit();
  }
?>

Leave a comment

NOTE: Enclose quotes in <blockquote></blockquote>. Enclose code in <pre lang="LANG"></pre> (where LANG is one of these).