Skip to content

Latest commit

 

History

History
136 lines (101 loc) · 3.82 KB

README-tag.md

File metadata and controls

136 lines (101 loc) · 3.82 KB

Tag protocol

Build proxy cache keys using tags set by application, and let application invalidate all url for a given tag at once.

Usage

var tag = require('tag');

// setup a global app tag,

app.use(tag('app'));

app.post('/protected/purge', tag('app'), function(req, res, next) {
 // see "scope" for setting up permissions
 res.sendStatus(200);
});

// per-resource tagging

app.get('/api/collection', tag('zone'), appMw);
app.post('/api/collection', tag('zone'), appMw);

// multiple tags can be set
app.get('/api/other', tag('zone', 'all'), ...);

// a route can invalidate tags set on other routes
app.put('/api/sample', tag('all'), ...);

// a tag can depend on the route using req.locals or req.params replacement
// no replacement is made if no param is defined.
app.get('/:domain/list', tag(':domain'), ...);


app.use(function(req, res, next) {
  res.locals.test = "test";
  next();
}, tag('has-:test'));

// if the replaced parameter is null, the tag is not added

tag(...)(req, res, next) can also be called directly, next being optional.

The last argument of tag() can be a function replacing the default deciding when tags must be incremented:

function incFn(req) {
 return req.method != "GET";
}

Simplified access to cache-control directives is made available through tag.for(...) or tag(...).for(...) method, which accepts one argument:

A for() call has no effect if the previous tag() call actually did not insert a tag.

A middleware for disabling cache is also available with tag.disable(). If set, other calls to tag.for() on the same route will be ignored.

app.get('/api/stats', tag.for('1d'), appMw);
app.get('/api/user', tag('user-*').for('10min'), appMw);
app.get('/api/user', tag('user-*').for(3600), appMw);

app.get('/trigger', tag.disable(), ...); // disable cache

Cache protocol

Application tags resources by replying X-Upcache-Tag: mytag response header to set resource to latest known value for that tag, or X-Upcache-Tag: +mytag to increment the value known by the proxy for that tag.

Proxy stores mytag as a sub-variant tag key for that url, and stores that value (or zero if initial) for that tag. This is like a Vary: mytag where mytag actual value is stored internally.

The cache key formula is mytag=curval. Thus all tagged resources can be invalidated at once without any performance impact: the variants cache and the cache storage backend are both LRU caches, so they don't actually need to be purged - requests keys just need to be changed.

For now only the proxy knows the tags values.

Golden rule

Never set a max-age on a mutable resource (unless you know it's okay to serve it perempted), only set a tag.

Sample setup

// application-level tag, changes when application version changes
app.get('*', tag('app'));
// static files tag, changes upon application restart
app.get('*.*', tag('static'), express.static(...));
// dynamic tag, changes upon non-GET calls
app.use('/api/*', tag('dynamic'));

and invalidation of that tag can take place upon application restart:

app.post('/.upcache', function(req, res, next) {
  if (config.version != config.previousVersion) {
    console.info(`app tag changes because version changes from ${config.previousVersion} to ${config.version}`);
    config.previousVersion = config.version;
    tag('app')(req, res, next);
  } else {
    next();
  }
}, function(req, res, next) {
  if (!config.invalidated) {
    console.info(`static tag changes after restart`);
    config.invalidated = true;
    tag('static')(req, res, next);
  } else {
    next();
  }
}, function(req, res) {
  res.sendStatus(204);
});