【问题标题】:Adding retry to future sequence for running Databricks notebooks in parallel in Scala为在 Scala 中并行运行 Databricks 笔记本的未来序列添加重试
【发布时间】:2020-10-30 11:18:55
【问题描述】:

我使用 Databricks 本身的以下代码来了解如何在 Scala 中并行运行其笔记本,https://docs.databricks.com/notebooks/notebook-workflows.html#run-multiple-notebooks-concurrently。我正在尝试添加重试功能,如果序列中的一个笔记本失败,它将根据我传递给它的重试值重试该笔记本。

这是来自 Databricks 的并行笔记本代码:

//parallel notebook code

import scala.concurrent.{Future, Await}
import scala.concurrent.duration._
import scala.util.control.NonFatal

case class NotebookData(path: String, timeout: Int, parameters: Map[String, String] = Map.empty[String, String])

def parallelNotebooks(notebooks: Seq[NotebookData]): Future[Seq[String]] = {
  import scala.concurrent.{Future, blocking, Await}
  import java.util.concurrent.Executors
  import scala.concurrent.ExecutionContext
  import com.databricks.WorkflowException

  val numNotebooksInParallel = 5
  // If you create too many notebooks in parallel the driver may crash when you submit all of the jobs at once. 
  // This code limits the number of parallel notebooks.
  implicit val ec = ExecutionContext.fromExecutor(Executors.newFixedThreadPool(numNotebooksInParallel))
  val ctx = dbutils.notebook.getContext()
  
  Future.sequence(
    notebooks.map { notebook => 
      Future {
        dbutils.notebook.setContext(ctx)
        if (notebook.parameters.nonEmpty)
          dbutils.notebook.run(notebook.path, notebook.timeout, notebook.parameters)
        else
          dbutils.notebook.run(notebook.path, notebook.timeout)
      }
      .recover {
        case NonFatal(e) => s"ERROR: ${e.getMessage}"
      }
    }
  )
}

这是我如何调用上述代码来运行多个示例笔记本的示例:

import scala.concurrent.Await
import scala.concurrent.duration._
import scala.language.postfixOps
val notebooks = Seq(
  NotebookData("Notebook1", 0, Map("client"->client)),
  NotebookData("Notebook2", 0, Map("client"->client))
)
val res = parallelNotebooks(notebooks)
Await.result(res, 3000000 seconds) // this is a blocking call.
res.value

【问题讨论】:

    标签: scala apache-spark future databricks azure-databricks


    【解决方案1】:

    这是一次尝试。由于您的代码无法编译,因此我插入了一些虚拟类。

    另外,您没有完全指定所需的行为,所以我做了一些假设。每个连接仅重试五次。如果任何一个 Future 在重试五次后仍然失败,那么整个 Future 都失败了。这两种行为都可以更改,但由于您没有指定,我不确定您想要什么。

    如果您有任何问题或希望我对程序进行更改,请在 cmets 部分告诉我。

    object TestNotebookData extends App{
      //parallel notebook code
    
      import scala.concurrent.{Future, Await}
      import scala.concurrent.duration._
      import scala.util.control.NonFatal
    
      case class NotebookData(path: String, timeout: Int, parameters: Map[String, String] = Map.empty[String, String])
    
      case class Context()
    
      case class Notebook(){
        def getContext(): Context = Context()
        def setContext(ctx: Context): Unit = ()
        def run(path: String, timeout: Int, paramters: Map[String, String] = Map()): Seq[String] = Seq()
      }
      case class Dbutils(notebook: Notebook)
    
      val dbutils = Dbutils(Notebook())
    
    
      def parallelNotebooks(notebooks: Seq[NotebookData]): Future[Seq[Seq[String]]] = {
        import scala.concurrent.{Future, blocking, Await}
        import java.util.concurrent.Executors
        import scala.concurrent.ExecutionContext
    
        // This code limits the number of parallel notebooks.
        implicit val ec = ExecutionContext.fromExecutor(Executors.newFixedThreadPool(numNotebooksInParallel))
        val ctx = dbutils.notebook.getContext()
    
        val isRetryable = true
        val retries = 5
    
        def runNotebook(notebook: NotebookData): Future[Seq[String]] = {
          def retryWrapper(retry: Boolean, current: Int, max: Int): Future[Seq[String]] = {
            val fut = Future {runNotebookInner}
            if (retry && current < max) fut.recoverWith{ _ => retryWrapper(retry, current + 1, max)}
            else fut
          }
    
          def runNotebookInner() = {
            dbutils.notebook.setContext(ctx)
            if (notebook.parameters.nonEmpty)
              dbutils.notebook.run(notebook.path, notebook.timeout, notebook.parameters)
            else
              dbutils.notebook.run(notebook.path, notebook.timeout)
          }
    
          retryWrapper(isRetryable, 0, retries)
        }
    
    
        Future.sequence(
          notebooks.map { notebook =>
            runNotebook(notebook)
          }
        )
      }
    
      val notebooks = Seq(
        NotebookData("Notebook1", 0, Map("client"->"client")),
        NotebookData("Notebook2", 0, Map("client"->"client"))
      )
      val res = parallelNotebooks(notebooks)
      Await.result(res, 3000000 seconds) // this is a blocking call.
      res.value
    }
    

    【讨论】:

    • 感谢您的回复!我通过一个修复应用了您的代码,缺少“val numNotebooksInParallel =”。 databricks 中也存在此错误:command-1656177338010954:38: error: type mismatch;发现:Throwable => scala.concurrent.Future[Seq[String]] 需要:PartialFunction[Throwable,scala.concurrent.Future[Seq[String]]] if (retry && current retryWrapper (重试,当前 + 1,最大值)}
    【解决方案2】:

    我发现这行得通:

    import scala.util.{Try, Success, Failure}
    
    def tryNotebookRun (path: String, timeout: Int, parameters: Map[String, String] = Map.empty[String, String]): Try[Any] = {
      Try(
        if (parameters.nonEmpty){
          dbutils.notebook.run(path, timeout, parameters)
        }
        else{
          dbutils.notebook.run(path, timeout)
        }
      )
    }
    
    //parallel notebook code
    
    import scala.concurrent.{Future, Await}
    import scala.concurrent.duration._
    import scala.util.control.NonFatal
    
    
    def runWithRetry(path: String, timeout: Int, parameters: Map[String, String] = Map.empty[String, String], maxRetries: Int = 2) = {
      var numRetries = 0
      while (numRetries < maxRetries){
        
        tryNotebookRun(path, timeout, parameters) match {
          case Success(_) => numRetries = maxRetries
          case Failure(_) => numRetries = numRetries + 1      
        }    
      }
    }
    
    case class NotebookData(path: String, timeout: Int, parameters: Map[String, String] = Map.empty[String, String])
    
    def parallelNotebooks(notebooks: Seq[NotebookData]): Future[Seq[Any]] = {
      import scala.concurrent.{Future, blocking, Await}
      import java.util.concurrent.Executors
      import scala.concurrent.ExecutionContext
      import com.databricks.WorkflowException
    
      val numNotebooksInParallel = 5
      // If you create too many notebooks in parallel the driver may crash when you submit all of the jobs at once. 
      // This code limits the number of parallel notebooks.
      implicit val ec = ExecutionContext.fromExecutor(Executors.newFixedThreadPool(numNotebooksInParallel))
      val ctx = dbutils.notebook.getContext()
      
      Future.sequence(
        notebooks.map { notebook => 
          Future {
            dbutils.notebook.setContext(ctx)
            runWithRetry(notebook.path, notebook.timeout, notebook.parameters)
          }
          .recover {
            case NonFatal(e) => s"ERROR: ${e.getMessage}"
          }
        }
      )
    }
    
    
    

    【讨论】:

    • 很高兴知道您的问题已解决。您可以接受它作为答案(单击答案旁边的复选标记将其从灰色切换为填充。)。这对其他社区成员可能是有益的。谢谢。
    猜你喜欢
    • 2023-03-18
    • 2022-11-26
    • 1970-01-01
    • 1970-01-01
    • 2020-12-21
    • 2023-02-13
    • 2023-03-24
    • 1970-01-01
    • 2022-06-23
    相关资源
    最近更新 更多