首页 > 解决方案 > 使用 R/Shiny,在死锁时自动刷新 dbGetQuery?

问题描述

我有一个(相当复杂的)Shiny 应用程序,它正在查询我们的一个 SQL 数据库中的数据,然后将该数据格式化为一系列 ggplots。我间歇性地从 Shiny 收到以下警告:[Microsoft][ODBC SQL Server Driver][SQL Server]Transaction (Process ID 62) was deadlocked on lock | 与另一个进程通信缓冲区资源,并已被选为死锁牺牲品。重新运行事务。

我只有大约 2% 的时间会收到这个错误。我相信我会陷入僵局,因为我正在从运营商不断更新的实时系统中查询数据。基本上,我认为这意味着我对这个错误无能为力。

我想对我的应用程序进行编程以检测死锁并自动重新查询数据本身。(我可以使用 validate 将错误消息替换为手动重新运行查询的指令,但这是不可取的,因为它需要手动干预。)

简化的应用程序:(我想我已经正确复制了相关部分,可能我错过了一些括号 - 代码显然不会为你运行,因为我已经编辑了 SQL 而你没有我的本地ODBC 连接建立。)

refreshintv = 600000

pool = dbPool(drv = odbc(), dsn = "[redacted]")

onStop(function() {
    poolClose(pool)
})

ui = fluidPage(
    dataTableOutput("df"),
)

server = function(input, output, session) {
    autorefresh = reactiveTimer(refreshintv)

    refresh = reactive({
        input$refresh #input$refresh comes from an action button that I haven't copied over for simplicity
        if("Auto Refresh" %in% input$options) {#input$options is similarly a checkboxGroupInput
            autorefresh()
        }
    })

    df = reactive({
        refresh()
        sql = "[insert sql code here]"
        df = dbGetQuery(pool, sql)
    })

}

app = shinyApp(ui = ui, server = server)

我的第一次尝试是在 df 反应式中尝试以下内容:

while (!("data.frame" %in% class(df))) {
    df = dbGetQuery(pool, sql)
}

但是上面的代码仍然出现死锁错误。似乎一旦闪亮遇到死锁错误,反应式就会返回错误,其余代码不会被评估。

我的第二种方法是创建一个反应循环。我或多或少知道这行不通,因为 df() 更改会导致 refresh() 无效,即使 class(df()) 没有更改。

refresh = reactive({
    input$refresh
    if("data.frame" %in% class(df())) {
        invalidateLater(1)
    }
    
    if("Auto Refresh" %in% input$options) {
        autorefresh()
    }
})

当然,这确实会产生无限递归错误:“评估嵌套太深:无限递归/选项(表达式=)?”

我能在闪亮的错误处理中找到的所有内容基本上只是显示错误消息。如何检查死锁错误并自动刷新或使 df() 无效?这甚至可能吗?

标签: rshiny

解决方案


推荐阅读