From 5a7ffcf499202d57266c71597c9ed7461ecfcdd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Tue, 15 Oct 2019 12:48:50 +0200 Subject: [PATCH] Be able to synchronize the TTL of cache-control header to expire in a coherent way --- lib/api/middlewares/cache-control-header.js | 35 +++++++++++++++++-- .../cache/cache-control-header-test.js | 14 ++++++-- 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/lib/api/middlewares/cache-control-header.js b/lib/api/middlewares/cache-control-header.js index d15b259c..a72bdea3 100644 --- a/lib/api/middlewares/cache-control-header.js +++ b/lib/api/middlewares/cache-control-header.js @@ -1,14 +1,35 @@ 'use strict'; -const ONE_YEAR_IN_SECONDS = 60 * 60 * 24 * 365; -const FIVE_MINUTES_IN_SECONDS = 60 * 5; +const ONE_MINUTE_IN_SECONDS = 60; +const THREE_MINUTE_IN_SECONDS = 60 * 3; +const FIVE_MINUTES_IN_SECONDS = ONE_MINUTE_IN_SECONDS * 5; +const TEN_MINUTES_IN_SECONDS = ONE_MINUTE_IN_SECONDS * 10; +const FIFTEEN_MINUTES_IN_SECONDS = ONE_MINUTE_IN_SECONDS * 15; +const THIRTY_MINUTES_IN_SECONDS = ONE_MINUTE_IN_SECONDS * 30; +const ONE_HOUR_IN_SECONDS = ONE_MINUTE_IN_SECONDS * 60; +const ONE_YEAR_IN_SECONDS = ONE_HOUR_IN_SECONDS * 24 * 365; + const FALLBACK_TTL = global.environment.varnish.fallbackTtl || FIVE_MINUTES_IN_SECONDS; +const validFallbackTTL = [ + ONE_MINUTE_IN_SECONDS, + THREE_MINUTE_IN_SECONDS, + FIVE_MINUTES_IN_SECONDS, + TEN_MINUTES_IN_SECONDS, + FIFTEEN_MINUTES_IN_SECONDS, + THIRTY_MINUTES_IN_SECONDS, + ONE_HOUR_IN_SECONDS +]; + module.exports = function setCacheControlHeader ({ ttl = ONE_YEAR_IN_SECONDS, fallbackTtl = FALLBACK_TTL, revalidate = false } = {}) { + if (!validFallbackTTL.includes(fallbackTtl)) { + throw new Error(`Invalid fallback TTL value for Cache-Control header. Got ${fallbackTtl}, expected ${validFallbackTTL.join(', ')}`); + } + return function setCacheControlHeaderMiddleware (req, res, next) { if (req.method !== 'GET') { return next(); @@ -27,7 +48,7 @@ module.exports = function setCacheControlHeader ({ if (everyAffectedTableCanBeInvalidated(affectedTables)) { directives.push(`max-age=${ttl}`); } else { - directives.push(`max-age=${fallbackTtl}`); + directives.push(`max-age=${computeNextTTL({ ttlInSeconds: fallbackTtl })}`); } if (revalidate) { @@ -49,3 +70,11 @@ function everyAffectedTableCanBeInvalidated (affectedTables) { affectedTables.getTables(skipNotUpdatedAtTables, skipAnalysisCachedTables) .every(table => table.updated_at !== null); } + +function computeNextTTL ({ ttlInSeconds } = {}) { + const nowInSeconds = Math.ceil(Date.now() / 1000); + const secondsAfterPreviousTTLStep = nowInSeconds % ttlInSeconds; + const secondsToReachTheNextTTLStep = ttlInSeconds - secondsAfterPreviousTTLStep; + + return secondsToReachTheNextTTLStep; +} diff --git a/test/acceptance/cache/cache-control-header-test.js b/test/acceptance/cache/cache-control-header-test.js index d3b89863..d761b6ef 100644 --- a/test/acceptance/cache/cache-control-header-test.js +++ b/test/acceptance/cache/cache-control-header-test.js @@ -79,7 +79,12 @@ describe('cache-control header', function () { return done(err); } - assert.equal(res.headers['cache-control'], `public,max-age=${ttl}`); + const cacheControl = res.headers['cache-control']; + const [ type, maxAge ] = cacheControl.split(','); + const [ key, value ] = maxAge.split('='); + + assert.ok(Number(value) <= ttl); + testClient.drain(done); }); }); @@ -114,7 +119,12 @@ describe('cache-control header', function () { return done(err); } - assert.equal(res.headers['cache-control'], `public,max-age=${ttl}`); + const cacheControl = res.headers['cache-control']; + const [ type, maxAge ] = cacheControl.split(','); + const [ key, value ] = maxAge.split('='); + + assert.ok(Number(value) <= ttl); + testClient.drain(done); }); });