繁体   English   中英

Python Task Scheduler Luigi可以检测间接依赖吗?

[英]Can Python task scheduler Luigi detect indirect dependencies?

简洁版本:

Python中是否有一个任务调度程序可以执行gmake的工作? 特别是,我需要一个任务调度程序来递归地解决依赖关系。 我调查了Luigi,但它似乎只能解决直接依赖关系。

长版:

我正在尝试构建一个以预定义顺序处理大量数据文件的工作流,以后的任务可能直接取决于一些更艰巨的任务的输出,但是,这些输出的正确性甚至取决于更艰巨的任务。

例如,让我们考虑如下的依赖关系图:

A <-B <-C

当我从任务C请求结果时,Luigi将自动安排B,然后由于B取决于A,因此它将安排A。因此,最终的运行顺序将是[A,B,C]。 每个任务都会创建一个官方输出文件,作为成功执行的标志。 第一次运行就可以了。

现在,假设我在任务A的输入数据中犯了一个错误。显然,我需要重新运行整个链。 但是,仅从A中删除输出文件将不起作用。 因为Luigi看到了B和C的输出,并得出结论,任务C的要求已得到满足,因此不需要运行。 我必须从所有依赖于A的任务中删除输出文件,以便它们可以再次运行。 在简单的情况下,我必须删除A,B和C中的所有输出文件,以便Luigi能够检测到对A所做的更改。

这是一个非常不方便的功能。 如果我有数十个或数百个彼此之间具有相当复杂的依赖关系的任务,那么当需要重新运行其中一个任务时,很难说出哪些任务受到了影响。 对于任务调度程序并具有解决依赖关系的能力,我希望Luigi能够像GNU-Make一样工作,其中递归检查依赖关系,并且在更改最深的源文件之一时将重建最终目标。

我想知道是否有人可以提供有关此问题的建议。 我是否缺少Luigi的一些关键功能? 还有其他充当gmake的任务计划程序吗? 我对基于Python的软件包特别感兴趣,并希望它们支持Windows。

非常感谢!

似乎可以通过覆盖完成任务的完整方法来实现。 您必须将其一直应用到依赖图中。

def complete(self):
    outputs = self.flatten(self.output())
    if not all(map(lambda output: output.exists(), outputs)):
        return False
    for task in self.flatten(self.requires()):
        if not task.complete():
            for output in outputs:
                if output.exists():
                    output.remove()
            return False
    return True

确实,这很不方便,并且d6tflow检查所有上游依赖项的完整性,而不仅仅是TaskC输出的存在。 如果重置TaskA,则TaskC也将不完整并自动重新运行。

# reset TaskA => makes TaskC incomplete
TaskA().invalidate() 
d6tflow.preview(TaskC()) # all tasks pending

有关更多详细信息,请参见下面的完整示例和d6tflow docs

import d6tflow
import pandas as pd

class TaskA(d6tflow.tasks.TaskCachePandas):  # save dataframe in memory

    def run(self):        
        self.save(pd.DataFrame({'a':range(10)})) # quickly save dataframe

class TaskB(d6tflow.tasks.TaskCachePandas):

    def requires(self):
        return TaskA() # define dependency

    def run(self):
        df = self.input().load() # quickly load required data
        df = df*2
        self.save(df)

class TaskC(d6tflow.tasks.TaskCachePandas):

    def requires(self):
        return TaskB()

    def run(self):
        df = self.input().load() 
        df = df*2
        self.save(df)

# Check task dependencies and their execution status
d6tflow.preview(TaskC())
'''
└─--[TaskC-{} (PENDING)]
   └─--[TaskB-{} (PENDING)]
      └─--[TaskA-{} (PENDING)]
'''

# Execute the model training task including dependencies
d6tflow.run(TaskC())

'''
===== Luigi Execution Summary =====

Scheduled 3 tasks of which:
* 3 ran successfully:
    - 1 TaskA()
    - 1 TaskB()
    - 1 TaskC()
'''

# all tasks complete
d6tflow.preview(TaskC())

'''
└─--[TaskC-{} (COMPLETE)]
   └─--[TaskB-{} (COMPLETE)]
      └─--[TaskA-{} (COMPLETE)]
'''

# reset TaskA => makes TaskC incomplete
TaskA().invalidate() 
d6tflow.preview(TaskC())
'''
└─--[TaskC-{} (PENDING)]
   └─--[TaskB-{} (PENDING)]
      └─--[TaskA-{} (PENDING)]
'''

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM