4
votes

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

1
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 rstudio.github.io/promises/articles/shiny.html - Pork Chop
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
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 shinyproxy.io - Pork Chop
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

1 Answers

2
votes

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

I took your app, added a visible progress bar, and deployed it with the above settings: https://minhealthnz.shinyapps.io/example_parallel/

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)