如何重置luigi任务状态?

时间:2016-01-05 13:50:06

标签: python luigi snakebite

目前,我有一堆排队的luigi任务,只有一个简单的依赖链(a -> b -> c -> d)。首先执行d,最后执行aa是被触发的任务。

a之外的所有目标都返回luigi.LocalTarget()个对象,并且只有一个通用luigi.Parameter(),它是一个字符串(包含日期和时间)。在luigi中央服务器上运行(已启用历史记录)。

问题在于,当我重新运行上述任务a时,luigi会检查历史记录并查看之前是否运行过该特定任务,如果状态为DONE,则不会运行任务(在这种情况下为d),我无法做到这一点,更改字符串是没有帮助(添加一个随机的微秒)。如何强制执行任务?

5 个答案:

答案 0 :(得分:10)

首先评论:Luigi任务是幂等的。如果您运行具有相同参数值的任务,无论您运行多少次,它都必须始终返回相同的输出。因此,不止一次运行它是没有意义的。这使得Luigi变得强大:如果你有一项重大任务会让很多事情花费很多时间并且在某个地方失败,那么你必须从头开始重新运行它。如果将它拆分为较小的任务,运行它并且失败,您只需要运行管道中的其余任务。

当您运行任务时,Luigi会检查该任务的输出以查看它们是否存在。如果他们不这样做,Luigi会检查它所依赖的任务的输出。如果它们存在,那么它将仅运行当前任务并生成输出Target。如果依赖项输出不存在,那么它将运行该任务。

因此,如果要重新运行任务,则必须删除其Target输出。如果要重新运行整个管道,则必须删除任务所依赖的所有任务的所有输出。

在Luigi存储库中有一个ongoing discussion in this issue。看看this comment,因为它会指向一些脚本来获取给定任务的输出目标并将其删除。

答案 1 :(得分:1)

@caners BaseTask 的改进,如果无法移除目标,则会引发错误。

class BaseTask(luigi.Task):
    force = luigi.BoolParameter(significant=False, default=False)

    def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)
    if self.force is True:
        outputs = luigi.task.flatten(self.output())
        for out in outputs:
            if out.exists():
                try:
                    out.remove()
                except AttributeError:
                    raise NotImplementedError


      

答案 2 :(得分:0)

d6tflow允许您重置并强制重新运行任务,请参见https://d6tflow.readthedocs.io/en/latest/control.html#manually-forcing-task-reset-and-rerun上的详细信息。

# force execution including downstream tasks
d6tflow.run([TaskTrain()],force=[TaskGetData()])

# reset single task
TaskGetData().invalidate()

# reset all downstream task output
d6tflow.invalidate_downstream(TaskGetData(), TaskTrain())

# reset all upstream task input
d6tflow.invalidate_upstream(TaskTrain())

注意:它仅适用于d6tflow任务和目标(已修改的本地目标),而不适用于所有luigi目标。应该走很长一段路,并且已针对数据科学工作流程进行了优化。对于本地员工来说效果很好,尚未在中央服务器上进行测试。

答案 3 :(得分:0)

我通常通过覆盖complete()来做到这一点:

class BaseTask(luigi.Task):

    force = luigi.BoolParameter()

    def complete(self):
        outputs = luigi.task.flatten(self.output())
        for output in outputs:
            if self.force and output.exists():
                output.remove()
        return all(map(lambda output: output.exists(), outputs))


class MyTask(BaseTask):
    def output(self):
        return luigi.LocalTarget("path/to/done/file.txt")

    def run(self):
        with self.output().open('w') as out_file:
            out_file.write('Complete')

运行任务时,将按预期创建输出文件。用force=True实例化类后,输出文件将仍然存在,直到调用complete()

task = MyTask()
task.run()
task.complete()
# True

new_task = MyTask(force=True)
new_task.output().exists()
# True
new_task.complete()
# False
new_task.output().exists()
# False

答案 4 :(得分:0)

我用它来强制重新生成输出,而无需先删除它,并允许您选择要重新生成的类型。在我们的用例中,我们希望旧生成的文件继续存在,直到用新版本重写它们为止。

# generation.py
class ForcibleTask(luigi.Task):
    force_task_families = luigi.ListParameter(
        positional=False, significant=False, default=[]
    )

    def complete(self):
        print("{}: check {}".format(self.get_task_family(), self.output().path))
        if not self.output().exists():
            self.oldinode = 0  # so any new file is considered complete
            return False
        curino = pathlib.Path(self.output().path).stat().st_ino
        try:
            x = self.oldinode
        except AttributeError:
            self.oldinode = curino

        if self.get_task_family() in self.force_task_families:
            # only done when file has been overwritten with new file
            return self.oldinode != curino

        return self.output().exists()

用法示例

class Generate(ForcibleTask):
    date = luigi.DateParameter()
    def output(self):
        return luigi.LocalTarget(
            self.date.strftime("generated-%Y-%m-%d")
        )

调用

luigi --module generation Generate '--Generate-force-task-families=["Generate"]'