Deixar o verniz enviar dados antigos do cache enquanto ele está buscando um novo?

7

Estou fazendo cache de páginas geradas dinamicamente (PHP-FPM, NGINX) e tenho verniz na frente deles, isso funciona muito bem.

No entanto, quando o tempo limite do cache é atingido, vejo isso:

  • nova página de solicitações de clientes
  • o verniz reconhece o tempo limite do cache
  • cliente aguarda
  • o verniz busca uma nova página do backend
  • o verniz entrega uma nova página ao cliente (e também tem a página armazenada em cache para a próxima solicitação que obtém instantaneamente)

O que eu gostaria de fazer é:

  • página de solicitações do cliente
  • o verniz reconhece o tempo limite
  • o verniz entrega a página antiga ao cliente
  • o verniz obtém uma nova página do backend e a coloca no cache

No meu caso, não é um site onde informações desatualizadas são um problema tão grande, especialmente quando falamos de um tempo limite de cache de alguns minutos.

No entanto, não quero punir o usuário para esperar na fila e sim entregar algo imediato. Isso é possível de alguma forma?

Para ilustrar, aqui está um exemplo de execução de cerco de 5 minutos contra o meu servidor que foi configurado para armazenar em cache por um minuto:

HTTP/1.1,200,  1.97,  12710,/,1,2013-06-24 00:21:06
...
HTTP/1.1,200,  1.88,  12710,/,1,2013-06-24 00:21:20
...
HTTP/1.1,200,  1.93,  12710,/,1,2013-06-24 00:22:08
...
HTTP/1.1,200,  1.89,  12710,/,1,2013-06-24 00:22:22
...
HTTP/1.1,200,  1.94,  12710,/,1,2013-06-24 00:23:10
...
HTTP/1.1,200,  1.91,  12709,/,1,2013-06-24 00:23:23
...
HTTP/1.1,200,  1.93,  12710,/,1,2013-06-24 00:24:12
...

Deixei de fora as centenas de solicitações em execução em 0.02 ou mais. Mas ainda me preocupa que haverá usuários tendo que esperar quase 2 segundos pelo seu HTML bruto.

Não podemos fazer melhor aqui?

(deparei-me com Envnish send while cache , parecia semelhante, mas não exatamente o que eu estou tentando fazer.)

Solução

A resposta de Shane Madden continha a solução, mas eu não percebi imediatamente. Houve outro detalhe que não incluí na minha pergunta porque achei que não era relevante, mas na verdade é.

A solução CMS que estou usando atualmente tem um ouvinte de banco de dados de verniz e, portanto, tem a capacidade de notificar o verniz para banir páginas cujo conteúdo foi alterado. Enviou uma solicitação PURGE com algum regex para banir certas páginas.

Para resumir as coisas, há dois casos em que eu tive usuários sem sorte:

  1. o verniz normal TTL de uma página expira
  2. usuários de back-end alteram o conteúdo, isso envia uma solicitação de exclusão para o verniz

Em ambos os casos, estou tendo usuários "sem sorte". No segundo caso, é aliviado pelo fato de que os usuários de back-end geralmente verificam a página depois que ela foi alterada; mas não necessariamente.

No entanto, para o segundo caso, criei uma solução (sim, percebo que essa pergunta começou com a busca de uma resposta para o primeiro caso ... pergunta mal formulada de minha parte):

Em vez de enviar uma solicitação de limpeza, usei a sugestão de Shanes e ajustei a VCL para que meu ouvinte do banco de dados de verniz pudesse enviar uma solicitação especial para buscar uma página com hash_always_miss definido como true .

Com a arquitetura atual, não tenho realmente o luxo de fazer uma solicitação assíncrona real, mas com a ajuda de Como faço para fazer uma requisição GET assíncrona em PHP? Eu consegui criar uma requisição GET para verniz que não espera que a página seja carregada, mas é boa o suficiente para acionar o verniz para buscar a página do backend e armazená-la em cache.

O efeito líquido foi que o ouvinte do banco de dados enviou a solicitação para o verniz e enquanto eu estava pesquisando a página específica, ela nunca tornava meus pedidos "azarados", mas uma vez que o verniz buscava a página completamente (isso pode variar de 300 ms a 2s) de repente estava lá.

Eu ainda tenho que encontrar uma solução para evitar os mesmos problemas quando o TTL normal se esgota, mas eu acho que a solução também é exatamente como Shane sugere: usar o wget para acionar o hash_always_miss , eu vou ter que seja esperto o suficiente para obter uma lista de páginas que eu tenho que atualizar.

    
por mark 24.06.2013 / 00:34

3 respostas

3

A solução que usei para resolver esse problema é garantir que o TTL em uma página nunca expire antes de ser atualizado - forçando um cliente HTTP em execução em um dos meus sistemas a obter a carga lenta em vez de um pedido de cliente sem sorte.

No meu caso, isso envolve wget em um cron, enviando um cabeçalho especial para marcar as solicitações e configurando req.hash_always_miss com base nisso, forçando uma nova cópia do conteúdo a ser buscada no cache.

acl purge {
    "localhost";
}

sub vcl_recv {
    /* other config here */
    if (req.http.X-Varnish-Nuke == "1" && client.ip ~ purge) {
        set req.hash_always_miss = true;
    }
    /* ... */
}

Para o seu conteúdo, isso pode significar configurar o TTL do Varnish para algo como 5 minutos, mas ter um wget do cron configurado para fazer uma solicitação de atualização do cache a cada minuto.

    
por 24.06.2013 / 00:53
4

@EDIT:

Apenas um rápido para informar que esse recurso parece ter sido implementado apenas na versão mais recente da ramificação principal; é provável que sua versão talvez não ofereça suporte stale-while-revalidate ainda / o exemplo que eu postei serviria 9999/10000 pedidos com um pobre bugger ainda tendo que esperar pela requisição para completar no backend (Ainda melhor que nada;) ...

Bem, não tenho 100% de certeza de que os comentários anteriores estejam dizendo que não está funcionando, mas de acordo com: link

  • req.grace - define por quanto tempo um objeto pode estar atrasado para o verniz continuar a considerá-lo para o modo de tolerância.
  • beresp.grace - define por quanto tempo o verniz beresp.ttl-time manterá um objeto.
  • req.grace - é frequentemente modificado em vcl_recv com base no estado do backend.

Atualmente estou usando configuração como o que o manual diz e está funcionando bem ... Aqui está um trecho do meu arquivo vcl ...

sub vcl_recv {
    # Cache rules above here...
    if (req.backend.healthy) {
        set req.grace = 30d;
    } else {
        set req.grace = 300d;
    }
}

sub vcl_fetch {
    # Fetch rules above here ...

    # If backend returns 500 error then boost the cache grace period...
    if (beresp.status == 500) {
        set beresp.grace = 10h;
        return (restart);
    }

    # How long carnish should keep the objects in cache..
    set beresp.grace = 1h;

    # Actual TTL of cache - If an object is older than this an update will be triggered to the backend server :)
    set beresp.ttl = 1m;
}

Observe que, se você quiser fornecer um período de tempo de resposta de resposta de backend mais longo (para 500 erros, como na minha configuração), será necessário configurar a análise de back-end ... Aqui está uma cópia do meu probe de back-end.

backend default {
    .host = "127.0.0.1";
    .port = "8080";
    .probe = { 
        .url = "/nginx-status";
        .timeout = 500 ms; 
        .interval = 3s; 
        .window = 10;
        .threshold = 4;
    }
}
    
por 30.07.2013 / 10:57
0

No verniz 3 isto é conseguido através do "modo de graça". De acordo com o manual [1], você precisa adicionar a seguinte lógica:

sub vcl_fetch {
  set beresp.grace = 30m;
} // Makes objects to be cached/stored 30 min beyond its max-age/ttl

sub vcl_recv {
  set req.grace = 60s;
} // Allows varnish to serve objects which expired within last minute.

[1] link

    
por 24.06.2013 / 17:45