I am trying to use the standard R shiny progress bar in a parallel foreach loop using the doParallel back-end. However, this results in the following error message:
Warning: Error in {: task 1 failed - "'session' is not a ShinySession object."
Code (minimum working example)
library(shiny)
library(doParallel)
ui <- fluidPage(
actionButton(inputId = "go", label = "Launch calculation")
)
server <- function(input, output, session) {
workers=makeCluster(2)
registerDoParallel(workers)
observeEvent(input$go, {
Runs=c(1:4)
Test_out=foreach(i=Runs, .combine=cbind, .inorder=TRUE, .packages=c("shiny"),.export=c("session")) %dopar% {
pbShiny = shiny::Progress$new()
pbShiny <- Progress$new(session,min = 0, max = 10)
on.exit(pbShiny$close())
test_vec=rep(0,100)
for(i in 1:10){
test_vec=test_vec+rnorm(100)
pbShiny$set(message="Simulating",detail=paste(i),
value=i)
Sys.sleep(0.2)
}
}
})
}
shinyApp(ui = ui, server = server)
The code runs if I run the foreach loop sequentially (using registerDoSEQ()).
Does anyone know how to resolve this issue?
Overall Goal
Show progress to user in a parallel foreach loop using the doParallel
back-end in shiny
User should be aware of the number of workers and
the progress per worker and or overall progress
There is a similar question under the following link, but it didn't get resolved as no working example was provided:
Utilizing parallel foreach for progress bar in R Shiny
The doParallel package is an extension of the parallel package as shown in the documentation here.
https://cran.r-project.org/web/packages/doParallel/doParallel.pdf
Reading the parallel package's documentation we see that it implements 3 different methods to achieve parallelism. Keep in mind R is a single threaded language.
A new R session where the parent process communicates with a worker or child process.
Via Forking
Using OS level facilities
You can find this information here,
https://stat.ethz.ch/R-manual/R-devel/library/parallel/doc/parallel.pdf
A consequence of this is that the child process cannot communicate with the parent process until it completes its computation and returns a value. This is to the best of my knowledge.
Hence, ticking the progress bar within the worker process will not be possible.
Full disclosure, I have not worked with the doParallel package and the documentation with respect to shiny was limited.
Alternative solution
There is a similar package however with extensive documentation with respect to shiny. These are the futures and promises and ipc packages. futures and promises enable asynchronous programming while ipc enables interprocess communication. To help us even more it also has an AsyncProgress() function.
Here is an example where we tick two counters synchronously.
Example
library(shiny)
library(future)
library(promises)
library(ipc)
plan(multisession)
ui <- fluidPage(
actionButton(inputId = "go", label = "Launch calculation")
)
server <- function(input, output, session) {
observeEvent(input$go, {
progress = AsyncProgress$new(message="Complex analysis")
future({
for (i in 1:15) {
progress$inc(1/15)
Sys.sleep(0.5)
}
progress$close()
return(i)
})%...>%
cat(.,"\n")
Sys.sleep(1)
progress2 = AsyncProgress$new(message="Complex analysis")
future({
for (i in 1:5) {
progress2$inc(1/5)
Sys.sleep(0.5)
}
progress2$close()
return(i)
})%...>%
cat(.,"\n")
NULL
})
}
shinyApp(ui = ui, server = server)
Your code adapted
Here is the code you have written, slightly modified to spin off many asynchronous processes. Any work can be performed in the worker, such as the vector you create and add an rnorm too. (Not shown here)
library(shiny)
library(future)
library(promises)
library(ipc)
plan(multisession)
ui <- fluidPage(
actionButton(inputId = "go", label = "Launch calculation")
)
server <- function(input, output, session) {
observeEvent(input$go, {
Runs=c(1:4) #define the number of runs
progress = list() #A list to maintain progress for each run
for(j in Runs){
progress[[j]] = AsyncProgress$new(message="Complex analysis")
future({
for (i in 1:10) {
progress[[j]]$inc(1/10)
Sys.sleep(0.2)
}
progress[[j]]$close()
return(i)
})%...>%
cat(.,'\n')
}
NULL
})
}
shinyApp(ui = ui, server = server)
The code above is a modified version of the code found in the ipc documentation here:
http://htmlpreview.github.io/?https://github.com/fellstat/ipc/blob/master/inst/doc/shinymp.html
Additional Resources:
https://rstudio.github.io/promises/articles/overview.html
I think I found a solution for cases where the number of runs exceeds the number of cores.
I searched for nested future processes and found the following page:
https://cran.r-project.org/web/packages/future/vignettes/future-3-topologies.html
I changed my code as follows. This runs the jobs sequentially per core and updates the respective progress bars accordingly.
library(shiny)
library(future)
library(promises)
library(ipc)
library(listenv)
plan(list(multiprocess, sequential))
ui <- fluidPage(
actionButton(inputId = "go", label = "Launch calculation")
)
server <- function(input, output, session) {
observeEvent(input$go, {
x <- listenv()
Runs=12 #define the number of runs
N=availableCores()
Tasks=rep(0,N) #Number of sequential tasks per core
Tasks[1:(Runs-(ceiling(Runs/N)-1)*N)]=ceiling(Runs/N)
if((Runs-(ceiling(Runs/N)-1)*N)<N){
Tasks[(Runs-(ceiling(Runs/N)-1)*N+1):N]=ceiling(Runs/N)-1
}
progress = list() #A list to maintain progress for each run
for(j in 1:N){
for(l in 1:Tasks[j]){
progress[[(l-1)*N+j]] = AsyncProgress$new(message=paste("Complex analysis, core ",j," , task ",l))
}
x[[j]]%<-%{
for(l in 1:Tasks[j]){
for (i in 1:10) {
progress[[(l-1)*N+j]]$inc(1/10)
Sys.sleep(0.5)
}
progress[[(l-1)*N+j]]$close()
}
}
}
NULL
})
}
shinyApp(ui = ui, server = server)
Related
I've written code that runs a long calculation in which, as part of it, several UI elements are updated showing part of the progress and results of the calculations. I would like to make possible for the user to run more than one calculation at the same time and keep been able of checking the progress and results.
This is an example app code:
#EXAMPLE APP----
#Libraries----
library(shiny)
library(shinyjs)
#UI code----
ui <- fluidPage(
useShinyjs(),
sidebarLayout(
sidebarPanel(
actionButton(
'pressme',
'Press me'
)
),
mainPanel(
div(
id='inmainP'
)
)
)
)
#Server code----
server <- function(input, output, session) {
clicks <- reactiveValues()
clicks$count <- 0
##Adding the UI elements necesary when pressing the button----
observeEvent(input$pressme,{
clicks$count <- clicks$count + 1
insertUI(
'#inmainP',
'beforeEnd',
div(id=paste0('Pcontainer',clicks$count),
p(tags$b(paste0(clicks$count,'-Process'))),
style = 'border:solid thin black;text-align: center;')
)
insertUI(
paste0('#Pcontainer',clicks$count),
'beforeEnd',
textOutput(paste0('process',clicks$count,'1'))
)
insertUI(
paste0('#Pcontainer',clicks$count),
'beforeEnd',
textOutput(paste0('process',clicks$count,'2'))
)
insertUI(
paste0('#Pcontainer',clicks$count),
'beforeEnd',
textOutput(paste0('process',clicks$count,'3'))
)
###Creating input to observe to do long calculation----
#This is done because it is the only way that I've accomplished, in the real app,
#that new elements render before starting calculation
runjs(
paste0('
Shiny.setInputValue("start",',clicks$count,')
')
)
})
##Runing long calculation----
observeEvent(input$start,{
odd <- 1
even <- 1
for (i in 1:10) {
Sys.sleep(1)
html(paste0('process',clicks$count,'1'), i)
if((i%%2)==0){
html(paste0('process',clicks$count,'2'), even)
even <- even + 1
}else{
html(paste0('process',clicks$count,'3'), odd)
odd <- odd + 1
}
}
})
}
shinyApp(ui, server)
As you will notice this app runs perfectly but new processes only run once the ones before have finished. I would like to solve this using callr package, that I've managed to use in other long jobs in the app, but don't know how to use it properly here. Any other suggestions based on the use of other packages as promises or future are also welcome.
As I mention I've tried to solve this using callr but I've failed at traying to create eventReact(s) programmatically, necessary to run each background job (to my understanding). And I don't understand how to make UI update from background process. How do the background processes work in R?
Link to this question in RStudioCommunity.
The scenario I'm emulating with the below minimal example is allowing a user to engage with a Shiny App (click the numericInput control and see server-side events occur) while a long-running download is occurring (simulated with Sys.sleep(10) within downloadHandler).
In a synchronous setting, when the "Download" button is clicked, the user can still interact with UI elements, but other Shiny calculations (in this case, renderText), get put in a queue. I'd like the asynchronous setting, where the download occurs in the background, and users can still interact with the UI elements and get desired output (e.g. renderText).
I'm using callr::r_bg() to achieve asynchronicity within Shiny, but the issue is that my current code of the downloadHandler is incorrect (mtcars should be getting downloaded, but the code is unable to complete the download, 404 error message), I believe it's due to the specific way in which downloadHandler expects the content() function to be written, and the way I've written callr::r_bg() is not playing nicely with that. Any insights would be appreciated!
Reference:
https://www.r-bloggers.com/2020/04/asynchronous-background-execution-in-shiny-using-callr/
Minimal Example:
library(shiny)
ui <- fluidPage(
downloadButton("download", "Download"),
numericInput("count",
NULL,
1,
step = 1),
textOutput("text")
)
server <- function(input, output, session) {
long_download <- function(file) {
Sys.sleep(10)
write.csv(mtcars, file)
}
output$download <- downloadHandler(
filename = "data.csv",
content = function(file) {
x <- callr::r_bg(
func = long_download,
args = list(file)
)
return(x)
}
)
observeEvent(input$count, {
output$text <- renderText({
paste(input$count)
})
})
}
shinyApp(ui, server)
I figured out a solution, and learned the following things:
Because downloadHandler doesn't have a traditional input$X, it can be difficult to include reactivity in the traditional way. The workaround was to present the UI as a hidden downlodButton masked by an actionButton which the user would see. Reactivity was facilitated in the following process: user clicks actionButton -> reactive updates -> when the reactive finishes (reactive()$is_alive() == FALSE), use shinyjs::click to initiate the downloadHandler
Instead of placing the callr function within the downloadHandler, I kept the file within the content arg. There seems to be some difficulties with scoping because the file needs to be available within the content function environment
I'm using a reactive function to track when the background job (the long-running computation) is finished to initiate the download using the syntax: reactive()$is_alive()
The invalidateLater() and toggling of a global variable (download_once) is important to prevent the reactive from constantly activating. Without it, what will happen is your browser will continually download files ad infinitum -- this behavior is scary and will appear virus-like to your Shiny app users!
Note that setting global variables is not a best practice for Shiny apps (will think of a better implementation)
Code Solution:
library(shiny)
library(callr)
library(shinyjs)
ui <- fluidPage(
shinyjs::useShinyjs(),
#creating a hidden download button, since callr requires an input$,
#but downloadButton does not natively have an input$
actionButton("start", "Start Long Download", icon = icon("download")),
downloadButton("download", "Download", style = "visibility:hidden;"),
p("You can still interact with app during computation"),
numericInput("count",
NULL,
1,
step = 1),
textOutput("text"),
textOutput("did_it_work")
)
long_job <- function() {
Sys.sleep(5)
}
server <- function(input, output, session) {
#start async task which waits 5 sec then virtually clicks download
long_run <- eventReactive(input$start, {
#r_bg by default sets env of function to .GlobalEnv
x <- callr::r_bg(
func = long_job,
supervise = TRUE
)
return(x)
})
#desired output = download of mtcars file
output$download <- downloadHandler(filename = "test.csv",
content = function(file) {
write.csv(mtcars, file)
})
#output that's meant to let user know they can still interact with app
output$text <- renderText({
paste(input$count)
})
download_once <- TRUE
#output that tracks progress of background task
check <- reactive({
invalidateLater(millis = 1000, session = session)
if (long_run()$is_alive()) {
x <- "Job running in background"
} else {
x <- "Async job in background completed"
if(isTRUE(download_once)) {
shinyjs::click("download")
download_once <<- FALSE
}
invalidateLater(millis = 1, session = session)
}
return(x)
})
output$did_it_work <- renderText({
check()
})
}
shinyApp(ui, server)
Thanks #latlio for your great answer. I think it cloud be easily improved.
invalidateLater should be used very carefully and only WHEN needed. I use invalidateLater only once and moved it to the logical part where we are waiting for the result. Thus we are NOT invalidating the reactivity infinitely.
library(shiny)
library(callr)
library(shinyjs)
ui <- fluidPage(
shinyjs::useShinyjs(),
#creating a hidden download button, since callr requires an input$,
#but downloadButton does not natively have an input$
actionButton("start", "Start Long Download", icon = icon("download")),
downloadButton("download", "Download", style = "visibility:hidden;"),
p("You can still interact with app during computation"),
numericInput("count",
NULL,
1,
step = 1),
textOutput("text"),
textOutput("did_it_work")
)
long_job <- function() {
Sys.sleep(5)
}
server <- function(input, output, session) {
#start async task which waits 5 sec then virtually clicks download
long_run <- eventReactive(input$start, {
#r_bg by default sets env of function to .GlobalEnv
x <- callr::r_bg(
func = long_job,
supervise = TRUE
)
return(x)
})
#desired output = download of mtcars file
output$download <- downloadHandler(filename = "test.csv",
content = function(file) {
write.csv(mtcars, file)
})
#output that's meant to let user know they can still interact with app
output$text <- renderText({
paste(input$count)
})
#output that tracks progress of background task
check <- reactive({
if (long_run()$is_alive()) {
x <- "Job running in background"
invalidateLater(millis = 1000, session = session)
} else {
x <- "Async job in background completed"
shinyjs::click("download")
}
return(x)
})
output$did_it_work <- renderText({
check()
})
}
shinyApp(ui, server)
I would like to execute a very expensive function (called 'never_ending_calc' in the below example) in a separate R session in a shiny app. I already prepared the below code, it works fine, but my parent R session waits on the result of the promise object and this uses lots of resources.
If you check the task manager while the below test application is running, you will see that the parent R session uses one CPU thread on maximum capacity while just simply waiting on the results of the promise object.
How can I evaluate a promise in a way which does not use the resources of the parent R session? (It is also ok if I loose the connection between these two sessions.)
I tried the followings (none of them worked):
use different 'plan'
send a custom message (like a warning) from function 'never_ending_calc' on its first row to somehow stop the parent session to wait on the promise object
Here is the example:
library(shiny)
library(future)
library(promises)
library(future.callr)
never_ending_calc <- function(){
K = 1
for (i in 1:20){
K = K + i
Sys.sleep(5)
}
return(K)
}
ui <- fluidPage(
# App title ----
titlePanel("Test app"),
# Sidebar layout with input and output definitions ----
sidebarLayout(
# Sidebar panel ----
sidebarPanel(
# action button to start the long test calculation ----
actionButton(inputId = "start_test",
label = "Start test run")
),
mainPanel()
)
)
server <- function(input, output) {
observeEvent(input$start_test, {
# start execute the long calculation in another separate R session
future::plan(future.callr::callr)
long_calculation_future <<- future::future({
never_ending_calc()
})
promises::then(long_calculation_future,
onFulfilled = NULL,
onRejected = function(error){NULL})
future::plan(future::sequential)
# return NULL to keep the shiny app reactive
NULL
})
}
shinyApp(ui, server)
I have an inputButton than when you click it, 2 querys to mysql database are done. One is a heavy one (more than 10 secs) and the other is light (less than 0.01sec to get data).
As I want to show the result of this querys on shiny app, I have intendeed to use Promises and Future packages for asyncronous loading.
In the example that I show you of my code, I have simulated the SQL querys with the function heavyFunction, which is intended to simulate the heavy query and the ligth one time loads.
The issue is that this code is not working for me, because the results of the light query are not shown till the heavy query is done.
Note: In the Rstudio console, this code works perfect...
library(future)
library(promises)
plan(multiprocess)
heavyFunction <- function(n){
Sys.sleep(n)
print(n)
}
ui <- fluidPage(
actionButton("go","Show the data"),
textOutput("result0sec"),
textOutput("result10sec")
)
server <- function(input,output,session){
data0 <- eventReactive(input$go,{
heavyFunction(0)
})
data10 <- eventReactive(input$go,{
heavyFunction(10)
})
output$result0sec <- renderText({
data <- data0()
future(data)%...>%print()
})
output$result10sec <- renderText({
data <- data10()
print(data)
})
}
shinyApp(ui,server)
What I'm doing wrong?
Welcome to SO!
This thread discusses the same issue.
Please also see the detailed answer from Joe Cheng on GitHub.
The main problem you are experiencing is reflected by his following statement:
The goal, at least for this release of Shiny, is not to allow this
kind of intra-session responsiveness, but rather, inter-session; i.e.,
running an async operation won't make its owning session more
responsive, but rather will allow other sessions to be more
responsive.
However, there are ways to work around this behaviour by running the future in a background R process with e.g. library(callr) or more convenient library(future.callr) and it's plan(callr).
Here is a working version of your code:
library(future)
library(promises)
library(future.callr)
plan(callr)
heavyFunction <- function(n) {
Sys.sleep(n)
print(n)
}
ui <- fluidPage(
br(),
actionButton("go", "Show the data"),
br(), br(),
textOutput("result0sec"),
textOutput("result10sec")
)
server <- function(input, output, session) {
futureData <- reactiveValues(data10 = NULL)
data0 <- eventReactive(input$go, {
heavyFunction(0)
})
observeEvent(input$go, {
myFuture <- future({
heavyFunction(5)
})
then(
myFuture,
onFulfilled = function(value) {
futureData$data10 <<- value
},
onRejected = NULL
)
return(NULL)
})
output$result0sec <- renderText({
data0()
})
output$result10sec <- renderText({
req(futureData$data10)
})
}
shinyApp(ui, server)
The most important point here is to realize, that you shouldn't return your future directly, otherwise it will block all other actions - the observer returns nothing, it only has the side-effect of triggering the callback-function.
I am using an R package that, in addition to calculating and returning things, prints some very useful info to the console. For example, it prints what iteration it is on right now.
How could I print that console output directly to my UI?
Assume this is my UI:
ui <- shinyUI(
fluidPage(
titlePanel("Print consol output"),
sidebarLayout(
sidebarPanel(actionButton("go", "Go")),
mainPanel(
verbatimTextOutput("console_text")
)
)
)
)
My user clicks on actionButton “Go” and my package starts doing things - while sending stuff to the console at the same time. I guess, I want the content of the console to be saved as output$console_text - but I don’t know if that’s the right approach and how to do it.
I didn't want to make the code super-complicated. So, instead of a package, I created my own little printing function in Server.
server <- function(input, output, session) {
myfunction <- function(x) {
for(i in 1:x) cat(i)
return(x)
}
observeEvent(input$go, {
{
# This is probably wrong:
output$console_text <- renderPrint(onerun <- myfunction(20))
}
})
}
shinyApp(ui, server)
Thank you very much!