1.目前etl的fetch task策略是基于任务子孙任务数和任务优先级获得task list
2.然后遍历task list 查看任务是否具备执行条件
- 集群资源校验(yarn/hdfs)<如果这里有性能瓶颈,可以抽出来做公共接口map,每10s更新一次>
- 数据是否准备好(仅mysql task具备),解决主从延迟问题
- 任务开始时间
- 任务的父任务是否都执行成功
3.每10s fetch一次task,遍历一次基于<2>的逻辑
我们把任务的父任务执行状态判断放到最后是想降低数据库查询成本(如果没放到最后,可以在exec_log表中维护一个依赖是否校验的状态去动态变更来减少数据库轮训查找成本)
我们如何避免,如 a->b->c 依赖关系,a还没完成又去校验b,b又没通过,又去校验c这种情况呢(如果此树较大,我们又是基于子孙任务数排序的话,会出现这种无谓遍历数据库的情况)。如果我们没有维护全局树及树中各任务的状态的话(成本较高,要时刻保证内存中的树与mysql表的任务状态同步)。
我们可以这么做(较少数据库的无谓遍历),在任务初始化时把任务依赖的dag加载的map中,并只维护任务与其一级子任务的关系如(<1,[2,3,4]> 父任务id:1,子任务id:2,3,4),然后在任务a校验没通过时,把a的一级子任务加入到list(此处不能放入set中,以为不能使用去重的集合,一个子任务可能会有多个父任务)中,依次遍历按照如此逻辑,在这一轮遍历结束后清空list。(或者维护全局list,在此任务校验通过后,从set清除此任务的一级子任务)---此种策略适用于只基于子孙任务数的排序方式,如果还有基于权重的排序并且权重只更新了子任务而没有更新此子任务的上游所有父任务就会出现严重问题
索性不如在每次fetch时就拿出子与父的map关系及当时的任务状态,作为任务提交时的判断,这样每fetch一次只与数据库交互一次
有如下两张表,直接用sql可解决之前说的问题,注:<假如任务状态status=0 为成功>
denpend <parent_id,child_id>
exec_log <task_id,status>
- depend 表的父id join exec_log 表的task_id,那么depend.child_id, depend.parent_id(=exec_log.task_id), exec_log.status(父任务的状态)就出来了,group by depend.child_id ,sum(exec_log.status)=0 的depend.child_id可以跑,否则不可跑
此策略已上线,运行稳定!!!