错误时再次循环

时间:2016-07-28 15:45:48

标签: r google-bigquery

我试着阅读所有内容,但我有点陷入一个问题。 通过使用bigrquery,我为Google BigQuery创建查询以获取数据 - 遗憾的是,有时我的查询由于超时而无法正常工作。 Q是SQL-Query,BQ应该存储从BigQuery下载的数据。

每次tryCatch给我一个错误时,有没有人知道如何重新循环?

到目前为止我得到了这个:

BQ_Foo <- NULL
tryCatch(
{ 
repeat{
  BQ_Foo <- query_exec(Q_foo,"bigquery")
  if(is.list(BQ_Foo) == TRUE)break }
}
,error=function(e){cat("ERROR : Query not loaded!", "\n")}
)

编辑:

我再次尝试了我的第一种方法,这次我收到了这条错误消息:

  

curl :: curl_fetch_memory(url,handle = handle)出错:
  操作被应用程序回调

中止

有人知道如何处理吗?

3 个答案:

答案 0 :(得分:7)

广泛基于r2evans的回答,这里有withRestarts如何做同样的事情,有This blog post的一些帮助:

set.seed(2)
foo <- NULL
operation <- function(x,tries) {
  message(paste("x is",x,"remaining tries",tries))
  withRestarts(
    tryCatch({
      if (runif(1) < x) stop("fail!") else 1
    },
    error=function(e) { invokeRestart("retry")}),
    retry = function() { 
      message("Retrying")
      stopifnot(tries > 0)
      operation(x,tries-1)
    }
  )
}
> operation(0.9,5)
# x is 0.9 remaining tries 5
# Retrying
# x is 0.9 remaining tries 4
# Retrying
# x is 0.9 remaining tries 3
# Retrying
# x is 0.9 remaining tries 2
# Retrying
# x is 0.9 remaining tries 1
[1] 1

这是一种递归调用,所以你可以在再次调用函数之前做任何你想做的事。

您可以在tryCatch错误处理程序中以相同的方式执行此操作,使用重新启动处理程序的兴趣是调用特定函数,如果您有两个tryCatch,您想要几乎相同的处理程序行为,那么您可以添加参数并使用每个try catch的相同处理程序,即:

testfun <- function(x) {
  withRestarts({
    tryCatch(
      {
        ifelse(runif(1) < 0.5,stop("Error Message"),warning("Warning message"))
      },
      warning=function(e) { invokeRestart("logger", level="warning", message=e ) },
      error=function(e) { invokeRestart("logger", level="error", message=e ) } 
    )
    },
    logger = function(level,message) {
      message(date()," [",level,"]: ",message[['message']])
    }
  )
}

,并提供:

> set.seed(2)
> testfun()
Fri Jul 29 14:15:11 2016 [error]: Error Message
> testfun()
Fri Jul 29 14:15:12 2016 [warning]: Warning message
> testfun()
Fri Jul 29 14:15:13 2016 [warning]: Warning message
> testfun()
Fri Jul 29 14:15:13 2016 [error]: Error Message

这里主要关注的是记录器方法的分解和减少代码重复。

答案 1 :(得分:4)

天真的解决方案

你可能会开始尝试将重复/在外面 tryCatch时稍微天真的尝试,如下所示:

set.seed(2)
foo <- NULL
while (is.null(foo)) {
  foo <- tryCatch({
    if (runif(1) < 0.9) stop("fail!") else 1
  },
  error = function(e) { message("err"); NULL; }
  )
}
# err
# err
# err
# err
message("success: ", foo)
# success: 1

不幸的是,你介绍了循环永远不会返回的可能性。为防止这种情况发生,你可以尝试一个柜台......

不太天真的解决方案

set.seed(2)
foo <- NULL
max_attempts <- 3
counter <- 0
while (is.null(foo) && counter < max_attempts) {
  counter <- counter + 1
  foo <- tryCatch({
    if (runif(1) < 0.9) stop("fail!") else 1
  },
  error = function(e) { message("err"); NULL; }
  )
}
# err
# err
# err
if (is.null(foo)) message("final failure") else message("success: ", foo)
# final failure

现在这对你来说是更好的 ,但它可能仍会无意中在服务器上引入拒绝服务“攻击”。 (考虑“为什么”查询失败:如果是因为服务器暂时被淹没,那么即使对于一些有限的请求,你也会使事情变得更糟。)虽然它会让你慢下来,但是在繁忙的情况下服务器,暂停将减轻服务器的负担,并可能在失败之前为您提供更好的查询成功机会。

更好的解决方案

在网络用语中,重复重试时,小TCP数据包可能会导致拥塞(请参阅Nagle's Algorithm以获取快速参考)。使用某种形式的指数退避是常见的,为了防止两个(或更多)客户端同时执行完全相同的退避,一些客户端稍微抖动(例如,httr::RETRY)。

set.seed(2)
foo <- NULL
max_attempts <- 3
# borrowed from hadley/httr::RETRY
pause_cap <- pause_base <- 1
counter <- 0
while (is.null(foo) && counter < max_attempts) {
  if (counter > 0L) {
    length <- stats::runif(1, max = min(pause_cap, pause_base * (2 ^ counter)))
    message("sleeping ", round(length, 1))
    Sys.sleep(length)
  }
  counter <- counter + 1
  foo <- tryCatch({
    if (runif(1) < 0.9) stop("fail!") else 1
  },
  error = function(e) { message("err"); NULL; }
  )
}
# err
# sleeping 0.7
# err
# sleeping 0.2
if (is.null(foo)) message("final failure") else message("success: ", foo)
# success: 1

层的总结

有点草率的代码,但我希望你明白这一点。在没有某种形式的自我限制的情况下对网络查询进行循环可以很容易地升级为无意的DOS。

答案 2 :(得分:0)

根据您的想法,我创建了这个代码,这似乎有用 - 我只需要对它进行压力测试。

QFoo <- paste0('SQL Code', dateBQ, ' ')
BQFoo <- NULL
testfun <- function(QFoo) {
    withRestarts({
        tryCatch({
            query_exec(QFoo, "bigquery")
        },
        warning = function(e) { invokeRestart("logger", level="warning", message = e) },
        error = function(e) { invokeRestart("logger", level="error", message = e) })
    },
    logger = function(level, message) {
        message(date(), " [", level, "]: ", message[['message']])
    })
}
testfun(QFoo)