Spark作业返回后,投机任务能否继续运行?

时间:2016-07-19 20:30:25

标签: java scala apache-spark manifest

我遇到一个简单的Spark工作问题,在简化后看起来像这样。

JavaRDD<ObjectNode> rdd = pullAndProcessData();
ManifestFilesystem fs = getOutputFS();
List<WriteObjectResult> writeObjectResults = rdd.mapPartitions(fs::write).collect();
fs.writeManifest(Manifest.makeManifest(writeObjectResults));

我对这段代码的期望是,无论发生什么,当且仅当所有任务都完成并且已成功将其分区写入S3时,才会调用writeManifest。问题是,显然,一些任务是在清单之后写入S3,这应该永远不会发生。

ManifestFilesystem.write中,我删除现有的清单(如果有)以使其无效,因为正常的工作流程应为:

  • 将所有分区写入S3
  • 将清单写入S3

我怀疑它可能因为推测的任务而发生,在以下场景中:

  • 某些任务被标记为可推测并重新发送给其他奴隶
  • 所有推测的任务都返回至少一个他们被发送到的奴隶,但其中一些继续在较慢的奴隶上运行
  • Spark在任务中断之前不会中断任务或将collect的结果返回给驱动程序
  • 仍在运行的推测任务最终执行ManifestTimeslice.write并在编写分区之前删除清单

这有可能发生吗?有没有人对这种行为有另一种假设?

注意:使用内置数据发布方法不是一个选项

注意2:我实际上发现了this,这往往证实了我的直觉,但确认仍然很好,因为我没有使用标准的HDFS或S3读/写出方法的原因超出了本问题的范围。

2 个答案:

答案 0 :(得分:1)

Spark不会主动杀死投机任务。它只是等待任务完成并忽略结果。我认为你的推测任务完全有可能在collect电话后继续写作。

答案 1 :(得分:0)

在从Spark的角度意识到没有办法解决之后我会回答我自己的问题:在你有时间完成之前,你如何确保杀掉所有的投机任务?让它们完全运行实际上更好,否则它们可能会在写入文件时被杀死,然后会被截断。

有不同的可能方法:

  • this thread中的一些消息表明,一种常见做法是在执行原子重命名之前写入临时尝试文件(大多数文件系统都很便宜,因为它只是一个指针开关)。如果推测任务尝试将其临时文件重命名为现有名称(如果操作是原子的,则不会同时发生),则忽略重命名请求并删除临时文件。

  • 据我所知,S3不提供原子重命名。此外,尽管上述过程相当容易实现,但我们目前正在尝试将自制程序解决方案限制到最大程度,并使系统保持简单。因此,我的最终解决方案是使用jobId(例如,作业开始的时间戳)并将其传递给从属服务器并将其写入清单中。将文件写入FS时,将应用以下逻辑:

    public WriteObjectResult write(File localTempFile, long jobId) {
        // cheap operation to check if the manifest is already there
        if (manifestsExists()) {
             long manifestJobId = Integer.parseInt(getManifestMetadata().get("jobId"));
             if (manifestJobId == jobId) {
                 log.warn("Job " + jobId + " has already completed successfully and published a manifest. Ignoring write request."
                 return null;
             }
             log.info("A manifest has already been published by job " + jobId + " for this dataset. Invalidating manifest.");
             deleteExistingManifest();
        }    
        return publish(localTempFile);
    }