Осторожно, закэшировано!

Сделать мое приложение на порядок быстрее потратив на это пару часов.

Пережить нагрузку в
«черную пятницу».

Что кэшировать

Статика (HTTP запросы)

max-age

server

Cache-Control: max-age=2629000;
without cache

Etag

server

Etag: "686897696a7c876b7e";

client

If-None-Match: "686897696a7c876b7e";
without cache

Last-modified

server

Last-modified: Fri, 29 Jul 2016 00:00:00 GMT;

client

if-Modified-Since: Fri, 29 Jul 2016 00:00:00 GMT;

Что кэшировать

Статика (HTTP запросы)

Получение данных

Показать список сортов кофе

var app = express();

app.get('/', (req, res) => {
    fetch()


});


fetch()

[
    {
        name: 'Timor',
        region: 'Indonesia'
    },
    {
        name: 'Mocha',
        region: 'Yemen'
    },
    {
        name: 'Java',
        region: 'Indonesia'
    }
]
                        
var app = express();

app.get('/', (req, res) => {
    fetch()
        .then(calculate)

});


fetch() calculate()

[
    {
        name: 'Timor',
        region: 'Indonesia'
    },
    {
        name: 'Mocha',
        region: 'Yemen'
    },
    {
        name: 'Java',
        region: 'Indonesia'
    }
]
                        

{
    Indonesia: [
        'Timor',
        'Java'
    ],
    Mocha: [
        'Yemen'
    ]
}
                        
var app = express();

app.get('/', (req, res) => {
    fetch()
        .then(calculate)
        .then(data => res.json(data));
});

app.listen(3000);
without cache
without cache
without cache
without cache

Кэширование данных

var cache;

app.get('/', (req, res) => {
    if (cache) { return res.json(cache); }

    fetch()
        .then(calculate)
        .then(data => cache = data)
        .then(data => res.json(data));
});
Simple cache

Уменьшили время ответа

Cache without batching

Объединение запросов

var cache;

app.get('/', (req, res) => {
    cache = cache ||
            fetch().then(calculate);
    cache
        .then(data => res.json(data))
        .catch(() => cache = null);
});
Cache with batching

Уменьшили время ответа

Уменьшили нагрузку

lru-cache


var LRU = require('lru-cache');
var cache = LRU({ maxAge: 3000 });
        
app.get('/', (req, res) => {
    if (!cache.has('coffee')) {
        var data = fetch().then(calculate);
        cache.set('coffee', data);
    }
        
    cache.get('coffee')
        .then(data => res.json(data))
        .catch(() => cache.del('coffee'));
});

lru-cache

Уменьшили время ответа

Уменьшили нагрузку

Контролируем время жизни

Что кэшировать

Статика (HTTP запросы)

Получение данных

Результаты вычислений (Шаблоны)

Single cache
Single cache

{{# cache 'menu'}}
    <h1>Coffee</h1>
    <ul>
        {{#each items}}
        <li>{{name}}</li>
        {{/each}}
    </ul>
{{/cache}}
        

Handlebars.registerHelper('cache', (key, options) => {
    if (!cache.has(key)) {
        cache.set(key, options.fn(options.data.root));
    }

    return cache.get(key);
});
        

Эффективность кэша

«... кажется, эти данные нам нужны всегда и обновляются они редко ...»

Эффективность кэша

HitRate = 'из кэша' / 'число запросов'
Запрос Кэш Эффективность
/ 'coffee' ~99%
/:region 'Indonesia', 'Africa', ... ~98%
/:uid 'user1', 'user2', ..., 'user100500' ~1%

Разогрев кэша

Горячий старт


// ...
app.listen(3000);

var data = fetch().then(calculate);
cache.set('coffee', data);
        

SPA

without cache

<script>
    var data = {
        '/coffee': { ... },
        '/favorite': { ... },
        '/userData': { ... }
    };
</script>
        
function request(url) {
    return data[url]
      ? Promise.resolve(data[url])
      : $.get(url);
}
without cache

Кэш - вспомогательная компонента

function memoize(key, maxAge, fn) {
    if (cache.has(key)) {
        return Promise.resolve(cache.get(key));
    }
 
    return Promise.resolve()
        .then(fn)
        .then(result => {
            cache.set(key, result, maxAge);
            return result;
        });
}

Легко менять реализацию кэша

Настрока кэширования моделей

cache.set('speaker', {
    name: 'Жигалов Сергей',
    twitter: '@sergey_zhigalov',
    email: 'zhigalov@yandex-team.ru'
});
cache.set('assistant', {
    name: 'Мокеев Евгений'
});

Спасибо!