[英]Alternatives to using `DT:replaceData()` when `server=FALSE` on shiny application deployed via kubernetes
由於各種原因,我希望能夠在使用客戶端處理時使用代理數據表和 replaceData,即DT::renderDataTable(..., server = FALSE)
。
我有一個 shiny 應用程序/儀表板,它與數據庫通信並向用戶提供信息。 用戶可以在應用程序中填寫表格,該表格將添加到數據庫中,然后 shiny 應用程序通過查詢數據庫來更新數據以獲取新信息。
該應用程序目前正在通過 kubernetes 使用LoadBalancer
進行部署,目的是根據需要使用多個副本來擴展應用程序。 該應用程序沒有通過 Shinyproxy 運行。
目前,當應用程序由單個副本(進程)運行時,應用程序將表現得非常好並且能夠使用server=TRUE
。 但是,當我增加要運行的進程/副本的數量時,除非在renderDataTable
中指定了server=FALSE
,否則數據將無法呈現給用戶。 由於目前未知的原因,但我懷疑這可能是由於會話不粘到 IP
雖然代碼能夠在server = TRUE
時執行 function 很好,但如果我想允許多個用戶應用它們都不能共享一個進程,因為一旦建立多個連接,應用程序將變得非常慢。 因此,我可能需要使用server=FALSE
以便每個用戶都能夠以非常重要的功能細節為代價查看數據( replaceData
停止工作)。 應用程序的產品所有者堅持認為這種行為保持不變,因為存在的數據通常很大,並且需要一些列排序和分頁才能找到您想要查看的信息。 並且在提交表單時,如果我不使用replaceData
並從頭開始重建表,用戶以前的表 state 就會丟失。
所以雖然我可以拆除數據表並在observeEvent
中重新生成它
observeEvent(input$button, {
...
output$table = renderDataTable({DT::datatable(df(), selection = 'single', callback =
JS("$.fn.dataTable.ext.errMode = 'none';"))}, server = FALSE)
...
})
這將提供一種解決方案,即使它會相應地更新表,也會產生不利的行為。
這將創建一個帶有按鈕和表格的應用程序。 Select 在表格上一行然后點擊按鈕。 預期的行為是表格在所選行上更新為“new_content”。 這僅在server=TRUE
時有效,在server=FALSE
時不會發生任何事情。
library(shiny)
library(DT)
data(iris)
server <- function(input, output, session) {
iris$new_col = ''
df = reactive({iris})
output$table = renderDataTable({
DT::datatable(df(), selection = 'single',
callback = JS("$.fn.dataTable.ext.errMode = 'none';"))}, server = FALSE) # When TRUE code works fine,,,
proxy = dataTableProxy('table')
observeEvent(input$button, {
# This line would be replacing the write to a db
iris[input$table_rows_selected, 'new_col'] <- 'changed'
# This line would be replacing the query to the db to reflect changes the user (and potentially other users have made between loading the data previously.
df <- reactive({iris})
proxy %>% replaceData(df(), rownames = TRUE, resetPaging = FALSE)
})
}
ui <- fluidPage(
actionButton('button', 'Press Me'),
DT::DTOutput('table')
)
shinyApp(ui, server)
我已經對 SO 進行了相當廣泛的搜索,這是我能找到的最接近的問題: DT Editing in Shiny application with client-side processing (server = F) throws JSON Error但是這實際上並沒有得到回答,並提供了“它只是不起作用”。
我包括 yaml 文件,以防有一些 kubernetes boffins 知道如何通過一些巧妙的技巧專門解決上述問題。 所描述的問題可能源於在副本之間交換會話,因此數據被錯誤傳達,但老實說,我在 kubernetes 方面並不是最好的......如果是這種情況,那么我將能夠在 shiny 應用程序中使用 server=TRUE ,然后這個也可以解決問題。
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-appname
spec:
replicas: 5
selector:
matchLabels:
app: appname
template:
metadata:
labels:
app: appname
spec:
containers:
- name: appname
securityContext:
privileged: false
image: appname:latest
ports:
- name: http
containerPort: 3838
---
apiVersion: v1
kind: Service
metadata:
name: servive-appname
spec:
ports:
- name: http
port: 3838
protocol: TCP
targetPort: 3838
selector:
app: appname
type: LoadBalancer
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: ingress-appname
annotations:
nginx.org/websocket-services: "service-appname"
spec:
tls:
- hosts:
- appname.url.com
rules:
- host: appname.url.com
http:
paths:
- path: /
backend:
serviceName: service-appname
servicePort: 3838
我們可以嘗試結合input$table_rows_selected
的信息使用reactiveValues
。 根據請求, server
參數等於FALSE
。
library(shiny)
library(DT)
data(iris)
server <- function(input, output, session) {
iris$new_col = ''
df = reactiveValues(iris = iris)
output$table = renderDataTable({
DT::datatable(df$iris, selection = 'single',
callback = JS("$.fn.dataTable.ext.errMode = 'none';"))}, server = FALSE) # When TRUE code works fine,,,
observeEvent(input$button, {
# This line would be replacing the write to a db
df$iris[input$table_rows_selected, c('new_col')] <- 'changed!'
})
}
ui <- fluidPage(
actionButton('button', 'Press Me'),
DT::DTOutput('table')
)
shinyApp(ui, server)
這是一種客戶端方法,基於@jpdugo17 的答案和@TJGorrie 的初始示例,使用stateSave
選項在重新渲染時維護表 state。 selectPage
和updateSearch
可以與dataTableProxy
一起使用 - input$table_state$order
的 state 需要作為選項傳遞:
library(shiny)
library(DT)
data(iris)
iris$new_col <- ''
server <- function(input, output, session) {
DF = reactiveValues(iris = iris)
output$table <- DT::renderDataTable(expr = {
if (is.null(isolate(input$table_state))) {
DT::datatable(
DF$iris,
selection = 'single',
callback = JS("$.fn.dataTable.ext.errMode = 'none';"),
options = list(stateSave = TRUE)
)
} else {
# print(isolate(input$table_state$order))
DT::datatable(
DF$iris,
selection = 'single',
callback = JS("$.fn.dataTable.ext.errMode = 'none';"),
options = list(
stateSave = TRUE,
order = isolate(input$table_state$order),
paging = TRUE,
pageLength = isolate(input$table_state$length)
)
)
}
}, server = FALSE)
proxy <- dataTableProxy('table')
observeEvent(input$button, {
DF$iris[input$table_rows_selected, c('new_col')] <- 'changed!'
})
observeEvent(DF$iris, {
selectPage(proxy, page = input$table_state$start/input$table_state$length+1)
updateSearch(proxy, keywords = list(global = input$table_state$search$search, columns = NULL)) # see input$table_state$columns if needed
}, ignoreInit = TRUE, priority = -1)
}
ui <- fluidPage(
actionButton('button', 'Press Me'),
DT::DTOutput('table')
)
shinyApp(ui, server)
這是一篇相關文章。
如果您使用的是 kubernetes/ingress-nginx,則可以使用 cookies 實現 session 親和力。
https://kubernetes.github.io/ingress-nginx/examples/affinity/cookie/
但是從您的 yaml 中,您正在使用 nginx.org 的 kubernetes-ingress,然后您可以閱讀
https://github.com/nginxinc/kubernetes-ingress/blob/master/examples/session-persistence/README.md
但僅在 NGINX Plus 中支持。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.