Laravel 5

Concurrence de jobs

  • Avatar de pierrocknroll
    Membre depuis :
    12/08/2019
    Messages :
    7

    Salut à tous,
    Je travaille sur Laravel depuis longtemps et je le maîtrise bien, toutefois j'ai un problème assez complexe dont je ne trouve pas de solution "propre".

    J'ai un Model qui est modifié par le front via une API CRUD. Si le Model change, via un Observer, je dispatch un job assez lourd de traitement et d'upload de fichier. Le souci c'est que si le front modifie le Model plusieurs fois de suite, le job va se lancer plusieurs fois également et il peut y avoir des soucis de concurrence :

    • Première modification, le job A se lance
    • Deuxième modification, le job B se lance
    • Pour une raison quelconque (j'ai plusieurs serveurs qui traitent les jobs avec Horizon), le job B termine avant le job A, le fichier uploadé sera écrasé par le job A et donc ne sera pas à jour par rapport à la modification faîte en 2ème.

    Je ne sais pas trop comment m'en sortir pour éviter cela, est-ce que le job doit se lancer qu'une seule fois après un certain délai, etc..
    Merci

  • Avatar de Dom
    Membre depuis :
    17/07/2017
    Messages :
    20

    Salut, à l'AFUP de Lille, j'ai parlé exactement de cela avec Freek Van der Herten. Il était pas question de traitement lourd, mais la reflexion était la même, on parlait de lancer des notifs au changement d'un model.
    Nous n'avons pas trouvé comment "lier" des jobs entre eux, pour dire par exemple : je lance un job je détruis les autres qui font "la même chose".

    Nous avons pensé à utiliser la date de modification du model dans le job et de la comparer au model actuel, pour savoir si il fallait effectivement faire l'action. Je ne sais pas si pour toi cela peut fonctionner, car il s'agit d'un traitement lourd, donc quand il va commencé il n'y aura pas forcément de changement de date de modif

    Je pense qu'ils ne peuvent pas être chain, vue que c'est des appels API.

    Par contre, tu peux peut être mettre un lock sur ton model au début du traitement de ton job.
    Quand le deuxième job arrive il voit le lock de posé, il ne fait rien par rapport au traitement mais peut dispatch une copie de lui même avec un délai (genre 1 minute).
    Quand ce nouveau job arrive, il verifié le lock. si toujours locké, il fait le meme dispatch d'une copie de lui même, mais avec un délai plus long par exemple. Et au bout de X dispatch tu failed.
    Par contre, si il arrive, et qu'il voit pas le lock, il lock lui même le model et fait son traitement, comme cela il previent les autres.

    Qu'en penses tu ?

  • Avatar de Dom
    Membre depuis :
    17/07/2017
    Messages :
    20

    En fait encore plus simple : tu poses ton lock, et tu utilises --tries et la function retryUntil
    Pas besoin de copie en fait je pense

  • Avatar de bestmomo
    Membre depuis :
    07/04/2013
    Messages :
    2524

    Salut,

    Si c'est le même job on peut éviter l'overlap.

  • Avatar de Dom
    Membre depuis :
    17/07/2017
    Messages :
    20

    Tu peux l'utiliser sur des jobs ?
    Je crois que son jobs est lié à son model

  • Avatar de pierrocknroll
    Membre depuis :
    12/08/2019
    Messages :
    7

    Salut et merci pour votre interêt à mon problème, j'ai moins eu de succès sur Laracast ^^

    Effectivement, mon Job sera lié à une instance de mon Model, le problème de concurrence n'a pas lieu lorsque les modèles sont différents, mais bien quand c'est une même instance du Model qui est modifiée plusieurs fois de suite.

    J'ai essayé diverses solutions qui fonctionnent pas mal mais je suis pas ultra convaincu et je me suis toujours dit qu'il y avait sûrement mieux et peut être même gêré directement par Laravel.

    Par exemple, je dispatch mon Job avec un delai de X minutes et en mettant une clé dans Redis qui me sert de "lock".
    Si deux modifications sont faîtes de suite, le premier Job se lancera au bout de 5 minutes, et le second ne se lancera pas si la clé Redis est encore présente. Vu qu'il se lance avec un délai, il aura la seconde modification (je refresh le model dans le job).

    if (!Redis::exists($model->id)) {
    Redis::set($model->id, 1);
    Job::dispatch($model->id)->delay(now()->addMinutes(5));
    }

    Le souci c'est que le traitement se fait réellement 5 minutes après la modification, même si mon traitement après est assez lourd, ça fait 5 minutes de perdues.

    J'ai également vu quelques repos sympas comme :
    https://gist.github.com/cabloo/328e39a19afeaed1256f83bd4a0ba4bc
    https://github.com/mpbarlow/laravel-queue-debouncer

    Mais je ne sais pas trop si cela va vraiment solutionner mon problème

  • Avatar de pierrocknroll
    Membre depuis :
    12/08/2019
    Messages :
    7

    Je me permets de remonter, un avis là dessus ? merci

Vous ne pouvez pas répondre à ce sujet.