Laisser le vernis envoyer les anciennes données du cache pendant qu'il en récupère une nouvelle?


8

Je mets en cache des pages générées dynamiquement (PHP-FPM, NGINX) et j'ai du vernis devant elles, cela fonctionne très bien.

Cependant, une fois le délai d'expiration du cache atteint, je vois ceci:

  • page des nouvelles demandes des clients
  • le vernis reconnaît le délai d'expiration du cache
  • le client attend
  • le vernis récupère une nouvelle page depuis le backend
  • le vernis fournit une nouvelle page au client (et a également mis en cache la page pour la prochaine demande qui l'obtient instantanément)

Ce que je voudrais faire, c'est:

  • page des demandes des clients
  • le vernis reconnaît le délai d'attente
  • le vernis remet l'ancienne page au client
  • le vernis récupère la nouvelle page du backend et la place dans le cache

Dans mon cas, ce n'est pas un site où les informations obsolètes sont un gros problème, surtout pas lorsque nous parlons d'un délai d'expiration du cache de quelques minutes.

Cependant, je ne veux pas punir l'utilisateur pour faire la queue et plutôt livrer quelque chose immédiatement. Est-ce possible d'une manière ou d'une autre?

Pour illustrer, voici un exemple de sortie de siège en cours d'exécution 5 minutes contre mon serveur qui a été configuré pour mettre en cache pendant une minute:

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
...

J'ai laissé de côté les centaines de demandes en cours 0.02. Mais cela m'inquiète toujours qu'il y aura des utilisateurs qui devront attendre près de 2 secondes pour leur HTML brut.

Ne pouvons-nous pas faire mieux ici?

(Je suis tombé sur l' envoi de vernis lors de la mise en cache , cela semblait similaire, mais pas exactement ce que j'essaie de faire.)

Solution

La réponse de Shane Madden contenait la solution mais je ne l'ai pas réalisée tout de suite. Il y avait un autre détail que je n'ai pas inclus dans ma question parce que je pensais que ce n'était pas pertinent, mais en fait il l'est.

La solution CMS que j'utilise actuellement dispose d'un écouteur de base de données de vernis et a donc la capacité de notifier le vernis pour interdire les pages dont le contenu a changé. Il a envoyé une PURGEdemande avec quelques regex pour interdire certaines pages.

Pour résumer, il y a deux cas où j'ai eu des utilisateurs malchanceux:

  1. le vernis TTL normal d'une page expire
  2. les utilisateurs du backend modifient le contenu, cela envoie une demande de purge au vernis

Dans les deux cas, j'ai des utilisateurs "malchanceux". Dans le second cas, il est atténué par le fait que les utilisateurs du backend vérifient généralement la page après l'avoir modifiée; mais pas nécessairement.

Néanmoins, pour le deuxième cas j'ai créé une solution (oui, je me rends compte que cette question a commencé par chercher une réponse pour le premier cas ... question mal formulée de ma part):

Au lieu d'envoyer une demande de purge, j'ai utilisé la suggestion de Shanes et ajusté la VCL pour que mon écouteur de base de données de vernis puisse envoyer une demande spéciale pour récupérer une page avec hash_always_missset to true.

Avec l'architecture actuelle, je n'ai pas vraiment le luxe de faire une vraie demande asynchrone, mais avec l'aide de Comment faire une demande GET asynchrone en PHP? J'ai pu créer une demande GET pour vernir qui n'attend pas que la page soit chargée mais est assez bonne pour déclencher le vernis pour récupérer la page du backend et la mettre en cache.

L'effet net a été que l'écouteur de la base de données a envoyé la demande au vernis et pendant que je sondais la page spécifique, il n'a jamais fait de demandes "malchanceuses" mais une fois que le vernis a récupéré la page complètement depuis le backend (cela peut aller de 300 ms à 2 s), il était soudainement là.

Je dois encore trouver une solution pour éviter les mêmes problèmes lorsque le TTL normal s'épuise, mais je suppose que la solution est également exactement comme Shane le suggère: en utilisant wget pour déclencher le hash_always_miss, je devrai être assez intelligent pour obtenir la liste de pages que je dois rafraîchir.

Réponses:


3

La solution que j'ai utilisée pour résoudre ce problème est de s'assurer que le TTL sur une page n'a jamais une chance d'expiration avant d'être actualisé - forçant un client HTTP s'exécutant sur l'un de mes systèmes à obtenir la charge lente au lieu d'un client malchanceux demande.

Dans mon cas, cela implique wgetsur un cron, l'envoi d'un en-tête spécial pour marquer les demandes et le réglage en req.hash_always_missfonction de cela, forçant une nouvelle copie du contenu à être récupérée dans le 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;
    }
    /* ... */
}

Pour votre contenu, cela pourrait signifier définir le Varnish TTL sur quelque chose comme 5 minutes, mais avoir un wget cron'd configuré pour effectuer une demande d'actualisation du cache toutes les minutes.


Je pense que je comprends ce que tu veux dire. Cela fonctionnerait bien pour une seule page, mais pour les autres milliers? Impossible d'utiliser cron / wget à cette échelle.
marquez le

Intrinsèquement, vous devez déclarer au moins les pages que vous souhaitez garder à jour dans le cache. Compte tenu de cette liste, il n'y a aucune raison que wget dans un script cron ne puisse pas vous aider.
Falcon Momot

Je pense que c'est le défi avec cette "approche externe du vernis". Je dois finalement m'asseoir et prendre une décision et l'implémenter pour chaque page. Il n'y a aucun moyen de dire au vernis: "ttl est épuisé? Récupérer la nouvelle version mais jusqu'à ce que cela arrive, servir l'ancienne" ...? Hmm.
marquez le

4

@ÉDITER:

Juste un petit rapide pour vous faire savoir que cette fonctionnalité ne semble avoir été implémentée que dans la dernière version de la branche principale, il y a de fortes chances que votre version ne prenne pas encore en charge le vrai périmé pendant la revalidation / l'exemple que j'ai publié servirait 9999/10000 requêtes avec un pauvre bugger devant encore attendre la fin de la requête en backend (Encore mieux que rien;) ...


Eh bien, je ne suis pas sûr à 100% pourquoi les commentaires précédents disent que cela ne fonctionne pas, mais selon: https://www.varnish-software.com/static/book/Saving_a_request.html

  • req.grace - définit combien de temps un objet peut être en retard pour que Varnish le considère toujours pour le mode grâce.
  • beresp.grace - définit la durée après laquelle le vernis beresp.ttl-time conservera un objet.
  • req.grace - est souvent modifié dans vcl_recv en fonction de l'état du backend.

J'utilise actuellement une configuration comme ce que dit le manuel et ça fonctionne bien ... Voici un extrait de mon fichier 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;
}

Notez que si vous souhaitez fournir un temps de grâce de réponse plus long (pour une erreur de 500 comme dans ma configuration), vous devrez configurer la vérification du backend ... Voici une copie de ma sonde du backend ..

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

est-ce toujours valable pour vcl 4.0?
Gadelkareem

0

Dans Varnish 3, ceci est réalisé via le "mode Grace". Selon le manuel [1], vous devez ajouter la logique suivante:

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


Cela n'atteindra pas l'objectif d'OP, un client sera toujours retardé en attendant que le contenu soit récupéré depuis le backend, même si les autres clients derrière celui-ci recevront le résultat de grâce périmé. "Une fois le TTL expiré, le premier client qui demande le contenu devrait être bloqué pendant 15 secondes, tandis que le deuxième client devrait obtenir la copie honorée." - varnish-software.com/static/book/Saving_a_request.html
Pax

Désolé, je n'ai pas réalisé comment cela fonctionne réellement avec la première demande expirée.
NITEMAN

Peut-être que l'objectif souhaité peut être atteint avec un peu de triche ttl et de C en ligne afin de déclencher une "demande d'arrière-plan"
NITEMAN
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.