Carnet Wiki

Programmer un Valeurs de retour d’un cron sans dérive horaire ( fonction pour tâches périodiques )

Version 5 — Août 2022 — 86.248.xx.xx

La documentation https://programmer.spip.net/Declarer-une-tache présente comment déclarer une tâche cron et sa périodicité.

La fonction implémentant un cron doit renvoyer une valeur dont le calcul n’est pas évident dans certains cas.

Cette page présente comment s’assurer qu’un cron s’exécute tous les jours vise à peu près à la même heure , sans que les dérives d’heures ne s’additionnent collecter des exemples de calculs .

Rappel

(Voir https://programmer.spip.net/100 )
La date du prochain cron dépend de la valeur renvoyée par le génie ET de la déclaration de délais faite dans le pipeline taches_generales_cron.
-  Quand la valeur renvoyée par le génie est positive, la valeur précise est ignorée, c’est juste relancé avec le délai du pipeline
-  Quand elle est négative, la valeur absolue renvoyée indique l’heure considérée par le génie comme étant celle de la dernière exécution et à partir de laquelle va s’appliquer le délai spécifié dans le pipeline. Et si le résultat est déjà dépassé, c’est programmé pour être exécuté tout de suite (dés que possible). Sinon ça prend la queue.

Déclenchement chaque jour à la même heure environ , sans dérive des décalages

environ}}}

L’API pour les tâches cron permet de facilement déclarer des périodicités pour les déclenchements, mais il est plus délicat de déclarer des heures de déclenchements fixes chaque jour.

Voici une fonction qui aide à viser une heure fixe pour le déclenchement d’une tâche chaque jour J à une heure HH (exemple : 23).
-  Comme c’est un lancement quotidien, le délai pour ce cron est de 24h.
-  Dans le code, la constante CHAQUE_JOUR_H_DEBUT vaut l’heure de déclenchement HH = 23 .
Exemple : define ('CHAQUE_JOUR_H_DEBUT', 23);
-  Le jour suivant J+1, un nouveau lancement doit se faire à la même heure HH visée environ, sans que le décalage de J ne s’ajoute au nouveau décalage de J+1.
- Le délai de déclenchement réel de la tâche étant impossible à contrôler, on ne peut savoir quelle heure il est quand le cron se déclenche réellement : il peut être 23h, ou 23h10, ou minuit 30... c’est à dire le lendemain.

Cette fonction évite les dérives progressives en recalant le déclenchement chaque jour, mais elle ne garantit pas que le déclenchement se fasse précisément à l’heure programmée, car cela dépend de la fréquentation du site : sur un site sans fréquentation la tâche ne se déclenchera pas, sur un site ayant une faible fréquentation, elle se déclenchera en retard.

/**
 * La date programmée pour le prochain cron dépend de la valeur renvoyée par 
 * le génie ET de la déclaration de délais faite dans le pipeline 
 * <span class="base64" title="PGNvZGUgY2xhc3M9InNwaXBfY29kZSBzcGlwX2NvZGVfaW5saW5lIiBkaXI9Imx0ciI+dGFjaGVzX2dlbmVyYWxlc19jcm9uPC9jb2RlPg=="></span> ou dans le <span class="base64" title="PGNvZGUgY2xhc3M9InNwaXBfY29kZSBzcGlwX2NvZGVfaW5saW5lIiBkaXI9Imx0ciI+cGFxdWV0LnhtbDwvY29kZT4="></span>.
 * Quand la valeur renvoyée par le génie > 0, 
 *   la valeur précise est ignorée, le cron est juste relancé 
 *   avec le délai du pipeline
 * Quand c'est négatif, 
 *   la valeur absolue renvoyée indique l'heure considérée comme étant celle 
 *   de la dernière exécution, à partir de laquelle va s'appliquer le délai spécifié 
 *   dans le pipeline.
 *   Si le résultat est déjà dépassé, ça sera exécuté tt de suite (dés que possible)
 *   Et sinon ça prend la queue
 * Le cron est exécutable à la date indiquée, mais dans la réalité il est exécuté 
 * après ce moment, après un délai non garanti, qui dépend de la fréquentation 
 * du site.
 * Avec le temps, les délais s'additionnent et le cron se décale tout autour 
 * du cadran horaire.
 * genie_vise_heure permet d'éviter cette dérive et de maintenir l'exécution 
 * à peu près à une heure prévue.
 * 
 * Pour cela, genie_vise_heure calcule la valeur de retour d'un génie 
 * permettant sa prochaine exécution à la même heure 
 * (le lendemain ou le jour même si le cron a tardé ou a duré longtemps).
 *
 * @param int $h_debut_cejour       heure visé le jour même. Exemple : 23
 * @return float|int
 */
function genie_vise_heure(int $h_debut_cejour) {
  // timestamp de l'heure visée (23h) du jour courant 
  $h_debut_cejour = mktime($h_debut_cejour, 0, 0); 
  $now = time();
  // S'il est plus de 23h mais moins que minuit, c'est donc dans le passé 
  if ($now > $h_debut_cejour) {
    // Le prochain cron devra se relancer "demain"
    // On veut donc garder le délai normal (de 24h) spécifié par le pipeline
    // par rapport à $h_debut_cejour reçu = 23h ce soir
    return (0-$h_debut_cejour);
  }
  // Mais si on est avant $h_debut_cejour, il est par exemple 1h ou 5h du mat,
  // C'est parceque le cron a mis très longtemps à se déclancher (gros délai) 
  // ou à s'exécuter.
  // Le 23h visé par $j_debug_cejour, c'est le jour même, dans 22 ou 18h.
  // Pour obtenir l'exécution du prochain cron à cette heure visée,
  // comme heure d'exécution du cron courant 
  // (à partir de laquelle le délai normal sera appliqué)
  // il faut indiquer 23h hier.
  return (0 - ($h_debut_cejour - 24 * 3600));
}


/**
 * genie_vise_heure calcule la valeur de retour permettant une exécution du génie 
 * chaque jour à la même heure environ
 * @param $h_debut_cejour = mktime(CHAQUE_JOUR_H_DEBUT, 0, 0);  =HH, par ex 23
 * @return valeur utilisable comme valeur de retour du genie 
*/
function genie_vise_heure($h_debut_cejour) {
  $now = time();
  // $h_debut_cejour c'est l'heure de début visée du même jour,
  // ça peut être déjà passé ou pas encore
  if ($now > $h_debut_cejour) {
    // Si c'est passé, il faudra relancer "demain"
    // et donc on garde le délai de 24h par rapport à HH à ce soir
    return (0-$h_debut_cejour);
  }
  // sinon, c'est qu'il y a eu un grand retard
  // on veut relancer le jour même à HH environ
  // et donc il faut compenser le délai de 24h déjà ajouté à HH ce soir
  return (0 - ($h_debut_cejour - 24 * 3600)); 
}