¿Dejar que el barniz envíe datos antiguos de la memoria caché mientras busca uno nuevo?

8

Estoy almacenando en caché páginas generadas dinámicamente (PHP-FPM, NGINX) y tengo barniz frente a ellas, esto funciona muy bien.

Sin embargo, una vez que se alcanza el tiempo de espera de caché, veo esto:

  • nueva página de solicitudes de clientes
  • barniz reconoce el tiempo de espera de caché
  • el cliente espera
  • el barniz obtiene una nueva página del backend
  • barniz entrega una nueva página al cliente (y también tiene una página en caché para la siguiente solicitud que la obtiene al instante)

Lo que me gustaría hacer es:

  • página de solicitudes del cliente
  • el barniz reconoce el tiempo de espera
  • barniz entrega la página anterior al cliente
  • barniz obtiene una nueva página del backend y la coloca en la memoria caché

En mi caso, no es un sitio donde la información desactualizada es un problema tan grande, especialmente cuando hablamos del tiempo de espera de caché de unos pocos minutos.

Sin embargo, no quiero que el usuario castigado espere en la cola y entregue algo inmediato. ¿Es eso posible de alguna manera?

Para ilustrar, aquí hay una salida de muestra de ejecutar asedio 5 minutos contra mi servidor que se configuró para almacenar en caché durante un 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
...

Dejé fuera los cientos de solicitudes que se ejecutaban más 0.02o menos. Pero todavía me preocupa que haya usuarios que tengan que esperar casi 2 segundos para su HTML sin formato.

¿No podemos hacer algo mejor aquí?

(Me encontré con el envío de Varnish mientras estaba en caché , sonaba similar pero no exactamente lo que estoy tratando de hacer).

Solución

La respuesta de Shane Madden contenía la solución, pero no me di cuenta de inmediato. Hubo otro detalle que no incluí en mi pregunta porque pensé que no era relevante, pero en realidad lo es.

La solución CMS que estoy usando actualmente tiene una base de datos de barniz y, por lo tanto, tiene la capacidad de notificar a barniz para prohibir las páginas cuyo contenido ha cambiado. Envió una PURGEsolicitud con algunas expresiones regulares para prohibir ciertas páginas.

Para resumir, hay dos casos en los que tuve usuarios desafortunados:

  1. el barniz normal TTL de una página caduca
  2. los usuarios del backend cambian de contenido, esto envía una solicitud de purga al barniz

En ambos casos tengo usuarios "desafortunados". En el segundo caso, se alivia por el hecho de que los usuarios del backend generalmente revisan la página después de que se ha cambiado; pero no necesariamente

Sin embargo, para el segundo caso, creé una solución (sí, me doy cuenta de que esta pregunta comenzó buscando una respuesta para el primer caso ... pregunta mal formulada de mi parte):

En lugar de enviar una solicitud de purga, utilicé la sugerencia de Shanes y ajusté el VCL para que mi escucha de la base de datos de barniz pueda enviar una solicitud especial para buscar una página con hash_always_missset to true.

Con la arquitectura actual, realmente no tengo el lujo de hacer una solicitud asincrónica real, pero con la ayuda de ¿Cómo hago una solicitud GET asincrónica en PHP? Pude elaborar una solicitud GET para barnizar que no espera a que se cargue la página, pero es lo suficientemente buena como para activar el barniz para recuperar la página del back-end y almacenarla en caché.

El efecto neto fue que el oyente de la base de datos envió la solicitud al barniz y, mientras estaba sondeando la página específica, nunca hizo que mis solicitudes fueran "desafortunadas", pero una vez que el barniz recuperó la página completamente desde el backend (esto puede variar de 300 ms a 2 segundos) De repente estaba allí.

Todavía tengo que encontrar una solución para evitar los mismos problemas cuando se agota el TTL normal, pero supongo que la solución también es exactamente como sugiere Shane: usando wget para desencadenar hash_always_miss, tendré que ser lo suficientemente inteligente como para obtener la lista de páginas que tengo que actualizar.

marca
fuente

Respuestas:

3

La solución que he usado para resolver este problema es asegurarme de que el TTL en una página nunca tenga la oportunidad de caducar antes de actualizarse, lo que obliga a un cliente HTTP que se ejecuta en uno de mis sistemas a obtener una carga lenta en lugar de un cliente desafortunado solicitud.

En mi caso, esto implica wgetun cron, enviando un encabezado especial para marcar las solicitudes y la configuración req.hash_always_missbasada en esto, forzando una nueva copia del contenido que se va a buscar en el caché.

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 su contenido, esto podría significar configurar Varnish TTL en algo así como 5 minutos, pero tener un cronget wget configurado para hacer una solicitud de actualización de caché cada minuto.

Shane Madden
fuente
Creo que entiendo lo que quieres decir. Esto funcionaría bien para una sola página, pero ¿las otras miles? No se puede usar cron / wget en esa escala.
marca
Intrínsecamente, debe declarar al menos qué páginas desea mantener actualizadas en la memoria caché. Dada esta lista, no hay ninguna razón por la que wget en un script cron no pueda ayudarlo.
Falcon Momot
Creo que ese es el desafío con este "enfoque externo de barniz". Finalmente necesito sentarme y tomar una decisión e implementar esto para cada página. No hay forma de decirle al barniz: "¿ttl se acabó? Busque una nueva versión, pero hasta que eso ocurra, ¿sirve la anterior" ...? Hmm
marca el
4

@EDITAR:

Solo una pregunta rápida para hacerle saber que esta característica parece haberse implementado en la última versión en la rama maestra, es probable que su versión no admita una verdadera revalidación aún obsoleta / el ejemplo que publiqué serviría Las solicitudes 9999/10000 con un bugger pobre todavía tienen que esperar a que se complete la solicitud en el back-end (Aún mejor que nada;) ...


Bueno, no estoy 100% seguro de por qué los comentarios anteriores dicen que no está funcionando, pero de acuerdo con: https://www.varnish-software.com/static/book/Saving_a_request.html

  • req.grace : define cuánto tiempo debe estar un objeto atrasado para que Varnish todavía lo considere para el modo de gracia.
  • beresp.grace : define durante cuánto tiempo el barniz beresp.ttl-time mantendrá un objeto.
  • req.grace : a menudo se modifica en vcl_recv en función del estado del back-end.

Actualmente estoy usando la configuración como dice el manual y está funcionando bien ... Aquí hay un fragmento de mi archivo 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;
}

Tenga en cuenta que si desea proporcionar un período de gracia de respuesta de back-end más largo (para un error de 500 como en mi configuración) deberá configurar el sondeo de back-end ... Aquí hay una copia de mi sonda 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;
    }
}
David
fuente
¿Sigue siendo válido para vcl 4.0?
Gadelkareem
0

En Varnish 3 esto se logra a través del "modo Grace". De acuerdo con el manual [1], debe agregar la siguiente 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] https://www.varnish-cache.org/docs/3.0/tutorial/handling_misbehaving_servers.html#grace-mode

NITEMAN
fuente
Esto no logrará el objetivo de OP, un cliente todavía se retrasará esperando que el contenido se obtenga del back-end, a pesar de que a otros clientes detrás de ese se les dará el resultado de gracia obsoleto. "Una vez que caduca el TTL, el primer cliente que solicita el contenido debe estar atascado durante 15 segundos, mientras que el segundo cliente debe obtener la copia agraciada". - varnish-software.com/static/book/Saving_a_request.html
Pax
Lo siento, no me di cuenta de cómo funciona realmente con la primera solicitud caducada.
NITEMAN
Tal vez se pueda lograr el objetivo deseado con algunos trucos ttl y C en línea para disparar una "solicitud de antecedentes"
NITEMAN