Création d'un cron avec SNS

Ou comment complètement détourner l’usage du service Amazon SNS.

Mon besoin est simple : pouvoir exécuter du code toutes les 30s. Cette tâche commencera et s’arrêtera en fonction d’actions utilisateur. En java j’aurais simplement utilisé Quartz, mais là mon code est en PHP et hébergé sur Beanstalk d’Amazon.

Je n’ai pas trouvé de service simple à utiliser sur AWS pour faire ça. On trouve bien un peu de doc pour le faire avec Data Pipeline mais c’est loin d’être trivial. Après beaucoup d’hésitation j’ai décidé d’utiliser le service SNS.

Pour faire simple, SNS est un service de diffusion de message multi-canal. (doc complète ici )

SNS

Par contre il n’est pas fait pour envoyer un message périodiquement. Avec SNS on ne peut pas non plus dire “envoie ce message dans 30s”, le message est distribué de suite. Donc pour mon cas d’usage ce n’est pas vraiment l’idéal. J’aurais tellement aimé pouvoir dans mon code avoir un consommateur de message qui une fois le traitement fini s’envoie un nouveau message à lui même mais pour dans 30s.

Malheureusement ce n’est pas aussi simple et c’est lorsque j’ai regardé les politiques de retry pour les abonnements en http que j’ai trouvé une solution à mon problème. En effet, dans le cas d’un message envoyé sur un consommateur HTTP, si la requête retourne un code d’erreur (type 5xx) alors le message n’est pas considéré comme lu et va être représenté au même consomateur suivant une politique de retry configurable (toutes les infos sur la doc).

SNS

Et là on voit bien se profiler une tâche périodique

C’est, je vous l’accorde, assez border line. Mais ça a le mérite d’être simple et de fonctionner. Le seul inconvénient c’est que le nombre de retry est limité à 99. Donc dans mon cas mon cron s’arrêtait après 50 minutes. Il n’y a pas d’information dans le message envoyé par SNS du nombre de retry, par contre il y a sa date d’émission. Dans mon consommateur, j’ai donc codé qu’après 2 minutes  j’envoyais un nouveau message et retournais un code HTTP 200 pour consommer le message et arrêter les retry sur celui-ci. Au début j’avais mis 10 minutes, mais je ne sais pas pourquoi, même avec un code 200 les retry continuaient toujours sur le message. Avec 2 minutes (et donc moins de retry) aucun problème.

Cela donne cette architecture :

SNS

Et un diagramme de séquence pour bien comprendre le déroulement  :

SNS

Dans ce cas je configure la politique de retry avec les options suivantes :

"minDelayTarget" => 30,
"maxDelayTarget" => 30,
"numRetries" => 10,
"numMaxDelayRetries" => 0,
"numNoDelayRetries" => 0,
"numMinDelayRetries" => 0,
"backoffFunction"=> "linear"

Oui retourner une erreur 500 alors que le traitement s’est bien passé n’est pas très orthodoxe, Mais le système fonctionne bien, je peux voir sur la console SNS le nombre de Topic ouverts et donc le nombre de cron en cours d’exécution. C’est assez pratique. Si vous avez d’autres solutions je suis preneur ! (des services comme setcronjob ou même easycron sont trop limités au niveau requête par jour)