6

Minimal reproducible example:

library("shiny")

ui <- fluidPage(
  actionButton("button1", "Run 1"),
  actionButton("button2", "Run 2")
)

server <- function(session, input, output) {
  cat("session starts\n")
  observeEvent(input$button1, {
    cat("1 starts\n")
    Sys.sleep(15)
    cat("1 stops\n")
  })

  observeEvent(input$button2, {
    cat("2 starts\n")
    Sys.sleep(15)
    cat("2 stops\n")
  })
}

shinyApp(ui = ui, server = server)

Each button simulates running some long cpu-intensive algorithm.

  1. Run the app and open a session on one browser tab.
  2. Open another browser tab with another session for the running app.
  3. Start Run 1 in the first tab. Go to the second browser tab and start the Run 2.

The problem: The second button observer does not start independently. It waits until the first run is finished in the first session. I thought that shiny sessions are independent. How does shiny handle multiple shiny sessions per single R session? What if multiple users want to connect to the application at the same time?

How does one handle multiple users running the same app at the same time? Thanks

JonnyRobbie
  • 526
  • 5
  • 16
  • 2
    R is single threaded so as shiny, the requests will be queued by default. Shiny team has introduced `promises` and `futures` packages to enable users to do `async` operations https://rstudio.github.io/promises/articles/shiny.html – Pork Chop Mar 10 '20 at 13:53
  • I guess this is the only solution I see. I just thought that shiny inherently uses one of those parralelization packages to handle multiple sessions. This brings up the question: is it possible to use the 'future' package to wrap the whole server function inside a single promise? – JonnyRobbie Mar 10 '20 at 15:49
  • Dont underestimate the benefits of single threaded applications, theres a lot less to worry about. Node, the backend of `shiny`, is single threaded however it can easily support 50k concurrent users at any given time. Wrt to using promises around the server, short answer is probably no. If you want to wrap the app in a "standalone" application look into docker https://www.shinyproxy.io/ – Pork Chop Mar 10 '20 at 15:54
  • We have machine learning functions that can take up to several dozen of minutes. I knew that R is single threaded, but as I said, I assumed (wrongly - my bad) that shiny goes parallel. – JonnyRobbie Mar 10 '20 at 16:05

1 Answers1

2

Limit the number of connections per worker process, i.e. give each user their own R worker process. You can do this by setting the number of concurrent connections allowed per worker process to 1.

If you are deploying your app via shinyapps.io the instructions and further background is here: https://shiny.rstudio.com/articles/scaling-and-tuning.html

If you are deploying to your own shiny server the instructions and further background is here: https://support.rstudio.com/hc/en-us/articles/220546267-Scaling-and-Performance-Tuning-Applications-in-Shiny-Server-Pro

Minimal working example:

library("shiny")

ui <- fluidPage(
  actionButton("button1", "Run 1"),
  actionButton("button2", "Run 2")
)

server <- function(session, input, output) {

  observeEvent(input$button1, {

    withProgress(message = 'Run 1', detail = '', value = 0, {
        for (i in 1:15) {
            incProgress(1/15)
            Sys.sleep(0.25)
        }
    })

  })

  observeEvent(input$button2, {

    withProgress(message = 'Run 2', detail = '', value = 0, {
        for (i in 1:15) {
            incProgress(1/15)
            Sys.sleep(0.25)
        }
    })

  })
}

shinyApp(ui = ui, server = server)
Ash
  • 1,463
  • 1
  • 4
  • 7
  • Hi @Ash, may I ask how does ShinyServer handle download files for different users? For example, assuming my app allow user to download some generated data based on their inputs. The file is written as .csv through downloadHandler and the file name is set as "data.csv". Then if there are two users are clicking the download button at the same time, will they get their own data file or one of them will overwrite the other's? I learned each user will have his own session, but regarding to write a file, will they have their own path? – Yunzhao Xing Nov 09 '20 at 19:53
  • 1
    Hello @YunzhaoXing. That won't be an issue. Shiny won't mix data between users, regardless of whether there is more than one user per worker process. The only exception to this is if you are intentionally making your data global, i.e. using the <<- operator. – Ash Nov 09 '20 at 21:48
  • Hi @Ash, thanks for the explanation. I understand that the data will not be mixed between users, but I'm a little confused about the download file. In the shiny document, the description of "content" parameter in downloadHandler, it says "... a single argument file that is a file path (string) of a nonexistent temp file, and writes the content to that file path...". It seems shiny will first save the data into a temp file on the server and then let user to download it. Since all the app users will have the same download file name, will there be any conflicts? Will each user has a new path? – Yunzhao Xing Nov 10 '20 at 22:34
  • BTW, here are two documents links I referred: https://shiny.rstudio.com/reference/shiny/latest/downloadHandler.html; https://mastering-shiny.org/action-transfer.html – Yunzhao Xing Nov 10 '20 at 22:35
  • Are the performance tuning settings available for the open source version of Shiny Server? https://support.rstudio.com/hc/en-us/articles/220546267-Scaling-and-Performance-Tuning-Applications-in-Shiny-Server-Pro – dca Jan 19 '22 at 17:33