Rq: Le travail est exécuté avant la fin du travail "depends_on" lors de l'utilisation de Job.create et q.enqueue_job

Créé le 27 janv. 2021  ·  5Commentaires  ·  Source: rq/rq

Considérez l'extrait suivant ci-dessous et deux nœuds de calcul en cours d'exécution :

r = Redis(
    host="localhost",
    port=6379,
    db=0
)

q = Queue(connection=r)

id_1 = str(uuid4())

q.enqueue(func_1, job_id=id_1)
q.enqueue(func_2, depends_on=id_1)

Dans cet extrait, tout fonctionne comme prévu ; les deux travaux sont mis en file d'attente (un sur chaque travailleur) mais func_2 ne s'exécute pas avant que func_1 ne soit terminé.

Considérons maintenant l'extrait suivant, toujours avec deux nœuds de calcul :

r = Redis(
    host="localhost",
    port=6379,
    db=0
)

q = Queue(connection=r)

id_1 = str(uuid4())
id_2 = str(uuid4())

job_1 = Job.create(func_1, id=id_1, connection=r) 
job_2 = Job.create(func_2, id=id_2, depends_on=id_1, connection=r)
q.enqueue_job(job_1)
q.enqueue_job(job_2)

Lorsque j'exécute ce code, les deux tâches sont démarrées immédiatement, malgré la définition de l'argument depends_on .




Mini question bonus

Pourquoi est-il nécessaire de mettre connection= sur les jobs quand j'utilise Job.create ? L'instance Queue sait-elle pas déjà quelle connexion Redis utiliser ? Le code échoue sans que je définisse connection sur tous les travaux également.

Commentaire le plus utile

Je pense que ce problème devrait être rouvert car il semble toujours se produire (nouveau test échoué):
https://github.com/rq/rq/blob/9bef2aa5a49d894bd03bd772b670e207f8edd0ef/tests/test_queue.py#L530

Je suis d'accord avec @jtfidje qu'il ne s'agit pas de dépendances multiples liées, bien que quelques personnes l'aient documenté dans #1170.

Le nœud du problème est que enqueue_job ne vérifie pas du tout les dépendances.

Tous les 5 commentaires

Ce PR ajoute une fonction de multi-dépendance à RQ proprement dit. Vous devriez essayer ceci à la place.

Merci de répondre!

J'ai parcouru le PR, mais je ne vois pas comment ces changements résoudraient ce problème ? La seule façon dont j'ai réussi à résoudre ce problème était de créer une classe de file d'attente personnalisée et d'implémenter la fonction suivante en remplacement de l'appel initial enqueue_job :

def enqueue_custom(
        self, job: CustomJob, pipeline=None, at_front: bool = False
    ) -> CustomJob:

        # If a _dependent_ job depends on any unfinished job, register all the
        # _dependent_ job's dependencies instead of enqueueing it.
        #
        # `Job#fetch_dependencies` sets WATCH on all dependencies. If
        # WatchError is raised in the when the pipeline is executed, that means
        # something else has modified either the set of dependencies or the
        # status of one of them. In this case, we simply retry.
        job.set_status(FlowStatus.QUEUED)

        if job._dependency_id:
            with self.connection.pipeline() as pipe:
                while True:
                    try:

                        pipe.watch(job.dependencies_key)

                        dependencies = job.fetch_dependencies(watch=True, pipeline=pipe)

                        pipe.multi()

                        for dependency in dependencies:
                            if (
                                dependency.get_status(refresh=False)
                                != FlowStatus.FINISHED
                            ):
                                job.set_status(FlowStatus.DEFERRED, pipeline=pipe)
                                job.register_dependency(pipeline=pipe)
                                job.save(pipeline=pipe)
                                job.cleanup(ttl=job.ttl, pipeline=pipe)
                                pipe.execute()
                                return job

                        break
                    except WatchError:
                        continue
        return super().enqueue_job(job, pipeline, at_front=at_front)

Vous avez également ce bloc if dans enqueue_call , et je ne pourrais pas faire fonctionner les dépendances sans lui.

Avec le PR fusionné, vous pouvez faire queue.enqueue(my_func, depends_on=[job_1, job_2])

Oui je comprends (et j'aime vraiment cette fonctionnalité !)
Mais, dans la doc, vous donnez cet exemple :

from rq.job import Job

job = Job.create(count_words_at_url, 'http://nvie.com')
print('Job id: %s' % job.id)
q.enqueue_job(job)

# create a job with a predetermined id
job = Job.create(count_words_at url, 'http://nvie.com', id='my_job_id')

Cette façon de faire correspond _très_ bien à mes besoins, mais si je fais ce travail, les dépendances ne fonctionnent pas. Si c'est par conception, bien sûr, je devrai utiliser q.enqueue( ... ) .

Éditer:
Mes fonctions ( ie count_words_at_url ) sont chargées dynamiquement à partir d'un fichier de configuration, et sont donc attachées à ma classe Job lors de l'exécution.

Je pense que ce problème devrait être rouvert car il semble toujours se produire (nouveau test échoué):
https://github.com/rq/rq/blob/9bef2aa5a49d894bd03bd772b670e207f8edd0ef/tests/test_queue.py#L530

Je suis d'accord avec @jtfidje qu'il ne s'agit pas de dépendances multiples liées, bien que quelques personnes l'aient documenté dans #1170.

Le nœud du problème est que enqueue_job ne vérifie pas du tout les dépendances.

Cette page vous a été utile?
0 / 5 - 0 notes