luigi上游任务应运行一次,为下游任务集创建输入

时间:2017-05-10 21:36:23

标签: luigi data-pipeline

我有一个很好的直接工作管道,我在命令行上通过luigi运行的任务触发所有必需的上游数据获取和处理它的正确顺序,直到它逐渐渗透到我的数据库中。

class IMAP_Fetch(luigi.Task):
  """fetch a bunch of email messages with data in them"""
  date = luigi.DateParameter()
  uid = luigi.Parameter()
…
  def output(self):
    loc = os.path.join(self.data_drop, str(self.date))
    # target for requested message
    yield LocalTarget(os.path.join(loc, uid+".msg"))

  def run(self):
     # code to connect to IMAP server and run FETCH on given UID
     # message gets written to self.output()
…

class RecordData(luigi.contrib.postgres.CopyToTable):
  """copy the data in one email message to the database table"""
  uid = luigi.Parameter()
  date = luigi.DateParameter()
  table = 'msg_data'
  columns = [(id, int), …]

  def requires(self):
    # a task (not shown) that extracts data from one message  
    # which in turn requires the IMAP_Fetch to pull down the message
    return MsgData(self.date, self.uid) 

  def rows(self):
    # code to read self.input() and yield lists of data values 

好东西。不幸的是,第一次数据获取与远程IMAP服务器进行通信,并且每次获取都是新连接和新查询:非常慢。我知道如何在一个会话(任务实例)中获取所有单个消息文件。我不了解如何保持下游任务的原样,一次处理一条消息,因为需要一条消息的任务触发只获取一条消息,而不是获取所有可用消息。我提前为缺少明显的解决方案而道歉,但到目前为止,我一直难以理解如何保持我简单的愚蠢管道,但是在一次通话中让所有数据中的漏斗都在顶部。谢谢你的帮助。

1 个答案:

答案 0 :(得分:1)

根据您的解释,我缺少的是发送到uid任务的RecordData值列表来自哪里。对于此解释,我假设您有一组uid个值,您希望将这些值合并到一个ImapFetch请求中。

一种可能的方法是定义batch_id,另外还有uid,其中batch_id指的是您想要在单个会话中获取的消息组。存储uidbatch_id之间的关联由您决定。它可以是传递给管道的参数,也可以是外部存储的参数。您遗漏的任务MsgData,即requires方法返回单个ImapFetch任务且当前具有uid参数的任务,应该需要ImapFetch } batch_id参数的任务。 ImapFetch任务所需的第一个MsgData任务将检索与该uid关联的所有batch_id值,然后在单个会话中检索这些消息。所有其他MsgData任务都需要(并且等待)这一批ImapFetch来完成,然后他们将能够像管道的其余部分一样执行他们各自的消息。因此,调整批量大小可能对整体处理吞吐量很重要。

另一个缺点是它在批处理级别而不是单个项目级别的原子逻辑性较小,因为如果未成功检索到ImapFetch个值中的一个,则批处理uid将失败。

第二种方法是将Imap会话作为每个进程(worker)的更多单例资源打开,并使ImapFetch任务重用相同的会话。