How to run parallel SQL within a reactive in Shiny - r

Background: I have a Shiny app that generates one dataframe by merging the results of several SQL queries from different databases into one dataframe.
Each SQL query is held within a function and all of the functions run once a button is pressed.
The problem: Two of the queries take a while to run but because Shiny is the way it is, they run in serial whereas I suppose it would be a lot faster to get them to run in parallel.
Attempts: I tried firstly to get each query to run asynchronously but apparently that is really for scaling an application between users rather than within session asynchronous so I've ditched that approach and tried to use parallel processing with doParallel.
I started by using the parallel processing within each function but of course that is pointless because the parallel processing doesn't chop up a SQL query and run each separately, so then I thought I should add all the functions to a list and run them with a parallel processing.
library(shiny)
library(doParallel)
library(dplyr)
PendingApptdf<-reactiveValues(data = data.frame()) #dataframe for the pending data
myResdf<-reactiveValues(data = data.frame()) #Results dataframe
LatestResultsdf<-reactiveValues(data = data.frame()) #Final dataframe
ui.R
ui <- fluidPage(
titlePanel("Parallel SQL attempt"),
sidebarLayout(
sidebarPanel(
),
mainPanel(
actionBttn("LatestResults_btn",label = "Latest results",size="sm"),
DT::dataTableOutput("LatestResultsTable")
)
)
)
server.R
server <- function(input, output) {
#SQL number one:
PatientPendingAppt<-function(HospNums){
ch_PAD_PendingAppt<-odbcDriverConnect("DRIVER={*******};*******;Database=******")
sql_PAD_PendingAppt<-paste0("SELECT * from database",HospNums)
resultsetPAD_PendingAppt <- sqlQuery(ch_PAD_PendingAppt, sql_PAD_PendingAppt)
resultsetPAD_PendingAppt<-unique(resultsetPAD_PendingAppt)
odbcClose(ch_PAD_PendingAppt)
return(resultsetPAD_PendingAppt)
}
#SQL number two:
myRes<-function(HospNums){
ch_PAD_myRes<-odbcDriverConnect("DRIVER={*******};*******;Database=******")
sql_PAD_myRes<-paste0("SELECT * from database2",HospNums)
resultsetmyRes <- sqlQuery(ch_PAD_myRes, sql_PAD_myRes)
resultsetPAD_myRes<-unique(resultsetPAD_myRes)
odbcClose(ch_PAD_myRes)
return(resultsetPAD_myRes)
}
PendingAppt<-reactive({
myNumber<- '123456'
Pending<-PatientPendingAppt(myNumber)
PendingApptdf$data<-Pending
})
myResOP<-reactive({
myNumber<- '123456'
myResOP1<-myRes(myNumber)
myResdf$data<-myResOP1
})
####Latest results datatable####
output$LatestResultsTable = DT::renderDT({
outputPar <- foreach(i = 1:2, .packages="RODBC")%dopar%{
PendingAppt()
mergedEnoting<-mergedEnoting()
#merge the results here with rbind
}
LatestResultsdf<-outputPar
datatable(LatestResultsdf$data,
options = list(
dom = 't'),
)
})
}
# Run the application
shinyApp(ui = ui, server = server)
But this doesn't seem to work either and it tells me that it can't find the function PendingAppt.
Why is this? Is it because it is a reactive being run inside of the parallel processing. If so then how to rectify?
How can I run the two SQL functions so that they run concurrently rather than in serial?

You can use futures combined with promises to run the two queries in parallel, see scaling shiny with async.
Under SQL Server I used a WAIT FOR DELAY to simulate two long queries:
library(DBI)
library(shiny)
library(future)
library(promises)
plan(multisession,workers = 2)
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Async Test"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
selectInput("choice",
"Choice",
choice = list('choice 1','choice 2'))
),
# Show a plot of the generated distribution
mainPanel(
textOutput("data1"),
textOutput("data2")
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output,session) {
cat(nbrOfWorkers(),' workers launched \n')
# first query depending on Choice
data1 <-reactive({
input$choice
start <- Sys.time()
future({
conn <- dbConnect(***)
dbGetQuery(conn,("WAITFOR DELAY '00:00:05'"))
dbDisconnect(conn)
paste("Result from Task 1 : done in ", round(difftime(Sys.time(),start)), 's')
}) %...>% print() })
# second query depending on Choice
data2 <-reactive({
input$choice
start <- Sys.time()
future({
conn <- Connect(***)
dbGetQuery(conn,("WAITFOR DELAY '00:00:10'"))
dbDisconnect(conn)
paste("Result from Task 2 : done in ", round(difftime(Sys.time(),start)), 's')
}) %...>% print() })
output$data1 <- renderText({ data1()})
output$data2 <- renderText({ data2()})
}
# Run the application
shinyApp(ui = ui, server = server)
You can verify that the result of a 5s query and a 10s query is displayed in 10s and not in 15s :

Related

R Shiny - How to do multiple tasks at the same time in one session? Such as two calculations or two timer

How can R(Shiny) perform multiple tasks at the same time?
For example have a "clock" or timer in your UI which keeps on ticking while having an another "ObserveEvent" trigger/calculation running?
See the following example in Shiny below. When you press "click", the clock freezes since R starts focussing on the for loop.
ui <- fluidPage(
h2(textOutput("currentTime")),
actionButton("click","click")
)
server <- function(input, output, session) {
output$currentTime <- renderText({
invalidateLater(1000, session)
paste("The current time is", Sys.time())
})
observeEvent(input$click,{
showNotification("Computation started!", type = "error")
#Sys.sleep(2)
for(i in 1:(8*10^7)) 1 + 1
showNotification("Computation finished!", type = "error")
})
}
# Create Shiny app ----
shinyApp(ui, server)

Stream system() output to Shiny front-end (continuously)

How can I capture the output of an ongoing system() operation and stream it to the Shiny front-end in "real-time"?
intern=T captures the entire output in a character vector, but I would prefer to "listen" to the system output as it happens.
library(shiny)
ui <- fluidPage(
titlePanel("Stream the system output"),
sidebarLayout(
sidebarPanel(
actionButton("btn1",label = "Let's stream")
),
mainPanel(
textOutput("textstream_output")
)
)
)
server <- function(input, output, session) {
rv <- reactiveValues("textstream"=c(""))
output$textstream_output <- renderText({
rv$textstream
})
observeEvent(input$btn1,{
# problem: is evaluated after finish, not during operation
rv$textstream <- system("Rscript -e \"for(i in 1:5){ print(Sys.time()); Sys.sleep(1); };\"",
intern = T)
})
}
shinyApp(ui = ui, server = server)
When running the system command with intern=F, the R console continuously updates once per second. How can I establish that in Shiny, ideally without having to slice the system call into smaller chunks?
Possibly related:
Possible to show console messages (written with `message`) in a shiny ui?
Extend time progress bar displays message
R Shiny: mirror R console outputs to Shiny
reactiveTimer provides one approach. My guess is that your approach doesn't work because observeEvent only updates the reactive object once evaluation of the expression is completed. Here's my approach. I create a script that I want to run in the background, so_script.R, and divert the output to so_output.txt. We wish to see the contents of so_output.txt while the script is running.
cat('sink(file = "so_output.txt")
for (i in 1:10) {
cat(format(Sys.time(), format = "%H:%M:%S"), "\n")
Sys.sleep(1)
}
cat("*** EOF ***\n")
sink()
', file = "so_script.R")
Here's the Shiny app:
library(shiny)
ui <- fluidPage(
titlePanel("Stream the system output"),
sidebarLayout(
sidebarPanel(
actionButton("btn_start",label = "Let's stream"),
actionButton("btn_stop",label = "Stop")
),
mainPanel(
htmlOutput("textstream_output")
)
)
)
server <- function(input, output, session) {
rv <- reactiveValues(textstream = c(""),
timer = reactiveTimer(1000),
started = FALSE)
observeEvent(input$btn_start, {
rv$started <- TRUE
system2("Rscript", "so_script.R", wait = FALSE)
})
observeEvent(input$btn_stop, { rv$started <- FALSE })
observe({
rv$timer()
if (isolate(rv$started))
rv$textstream <- paste(readLines("so_output.txt"), collapse = "<br/>")
})
output$textstream_output <- renderUI({
HTML(rv$textstream)
})
}
shinyApp(ui = ui, server = server)
Each time the timer fires, we read in the contents of so_output.txt if streaming has started. Output:

How to force stop doParallel %dopar% calculations in shiny app

My shiny app does clustering using %dopar% from doParallel library. There is one thing that bothers me: if an operation takes, for example, 10 minutes to calculate, but user closes the window of an app before it's done - calculations will continue to happen until they're finished.
What I'm trying to do is to force stop them on session end. I tried to do something like this
session$onSessionEnded(function() {
stopCluster(clu)
print("stopped")
})
to stop calculations on window closure, but it is evaluated after the calculations are done. Here is a test app i'm trying to get it to work on:
server.r
library(shiny)
library(doParallel)
shinyServer(function(input, output, session) {
wd <- getwd()
rv <- reactiveValues()
clu <- makeCluster(2)
registerDoParallel(clu)
observeEvent(input$loadDataButton, {
rv$listDF <- readRDS(paste0(wd, "/iris15k.RData"))
})
observeEvent(input$clusterButton, {
if (input$clusterButton >= 1) {
df <- rv$listDF
rv$resultTime <- system.time(
rv$r <- foreach(icount(100)) %dopar% {
table <- kmeans(as.data.frame(df), centers = 3)
}
)
}
})
observeEvent(rv$listDF, {
output$done <- renderText("Done loading!")
})
observeEvent(rv$resultTime, {
output$time <- renderText(c("Done caltulating, time:", rv$resultTime))
print(rv$resultTime)
})
session$onSessionEnded(function() {
stopCluster(clu)
print("stopped")
})
})
ui.r
library(shiny)
shinyUI(fluidPage(
actionButton(
inputId = "loadDataButton",
label = "load data"
),
actionButton(
inputId = "clusterButton",
label = "Do clustering"
),
textOutput("done"),
textOutput("time")
))
iris15k.RData is a simple iris dataset with replicated rows to make the operation longer.
There is a way to write a bash script that will do the job and kill the processes in very straight way, but I'd rather do it from R side as I know little of it and it seems to be right to get it work withing an app code.
Any suggestions?

How to get iterative output from a "for" loop in R shinyapp?

I am looking for a solution in which we can print single iteration output in R shiny. Right now I got the output (a bunch of text output) when For loop ends its working. Is there any way to print itrative output to R shiny mainpanel from for loop ?
Edit 1. Here is a sample code I can not share original code due to official reason. Hope it helps.
library(shiny)
library(shinydashboard)
library(shinythemes)
ui <- dashboardPage(
dashboardHeader(title = "LAP"),
dashboardSidebar(
sidebarMenu(
#menuItem("Introduction", tabName = "intro", icon = icon("info-circle")),
menuItem("test", tabName = "FD", icon = icon("info-circle"))
)),
dashboardBody(
tabItems(
tabItem(tabName = "FD",
fluidRow(
box(verbatimTextOutput("loc") )))
)
)
)
server =shinyServer(function(input, output){
mydata<- reactive({
for(i in 1:100){
print("For Demo Purpose")
}
})
output$loc<- renderPrint({
mydata()
})
})
shinyApp(ui= ui, server = server)
The easiest way that I can think of would be to use the iterated function to append to a reactive variable, and display that variable in another output element.
Just keep output and generation separate. The output element will trigger a redisplay when the reactive value changes, but shouldn't cause odd loops to happen as long as it doesn't modify any variables.
Here's my initial attempt at an example, which doesn't work properly:
## stub UI with an activation button and text output element
ui <- fluidPage(
titlePanel("Iterative output demo"),
sidebarLayout(
sidebarPanel(
actionButton("start","Start Program")
),
mainPanel(
textOutput("showProgressText")
)
)
)
## server stub
server <- function(input, output) {
## reactive list to store result values
iterativeOutput <- reactiveValues();
## text rendering function `showProgressText`
output$showProgressText <- renderText({
sapply(iterativeOutput,paste);
})
## iterative function; modifies the reactive list on each iteration
observeEvent(input$start,{
iterativeOutput <- reactiveValues(); # clear list
for(i in 1:10){
iterativeOutput[[as.character(i)]] <-
sprintf("%2d: For Demo Purpose\n", i);
Sys.sleep(0.2);
}
})
}
This code outputs the modified information all in one go, rather than updating it.
I think the problem is likely to be that Shiny is waiting for the end of the function to check for reactive triggers. This is because, despite appearances, Shiny does not run operations in parallel. The only way to get two things working iteratively is to break the function, allow other functions to run, then resume the function. This is the process mentioned in the Google groups link.
The first implementation in that link works because a timer is set up to run one iteration of the function at one-second intervals. Here's an alternative implementation where the iterative function updates only [once] when a button is pressed:
## UI stub
ui <- fluidPage(
titlePanel("Iterative output demo"),
sidebarLayout(
sidebarPanel(
actionButton("start","Start Program")
),
mainPanel(
htmlOutput("showProgressText")
)
)
)
## Server stub
server <- function(input, output) {
iterativeOutput <- reactiveValues();
output$showProgressText <- renderText(
paste0(sapply(reactiveValuesToList(iterativeOutput),paste),
collapse="<br />\n")
)
observeEvent(input$start,{
#iterativeOutput <- reactiveValues(); # clear list
i <- (input$start-1) %% 10;
iterativeOutput[[as.character(i)]] <-
sprintf("%2d: %d", i, sample.int(1000, 1));
})
}
The insertUI approach is interesting, because it is creating a separate display loop in the middle of a method. I can imagine Shiny code becoming a lot less simple and predictable if that approach were used all over the place.

Calling a shiny JavaScript Callback from within a future

In shiny, it is possible to call client-side callbacks written in javascript from the server's logic. Say in ui.R you have some JavaScript including a function called setText:
tags$script('
Shiny.addCustomMessageHandler("setText", function(text) {
document.getElementById("output").innerHTML = text;
})
')
then in your server.R you can call session$sendCustomMessage(type='foo', 'foo').
Suppose I have a long-running function which returns some data to plot. If I do this normally, the R thread is busy while running this function, and so can't handle additional requests in this time. It would be really useful to be able to run this function using the futures package, so that it runs asynchronously to the code, and call the callback asyncronously. However, when I tried this is just didn't seem to work.
Sorry if this isn't very clear. As a simple example, the following should work until you uncomment the two lines trying to invoke future in server.R. Once those lines are uncommented, the callback never gets called. Obviously it's not actually useful in the context of this example, but I think it would be very useful in general.
ui.R:
library(shiny)
shinyUI(fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput("max",
"Max random number:",
min = 1,
max = 50,
value = 30)
),
mainPanel(
verbatimTextOutput('output'),
plotOutput('plot')
)
),
tags$script('
Shiny.addCustomMessageHandler("setText", function(text) {
document.getElementById("output").innerHTML = text;
})
')
))
server.R:
library(shiny)
library(future)
plan(multiprocess)
shinyServer(function(input, output, session) {
output$plot <- reactive({
max <- input$max
#f <- future({
session$sendCustomMessage(type='setText', 'Please wait')
Sys.sleep(3)
x <- runif(1,0,max)
session$sendCustomMessage(type='setText', paste('Your random number is', x))
return(NULL)
#})
})
})
Here is a solution on how you could use the future package in a shiny app.
It is possible to have multiple sessions with no session blocking another session when running a computationally intensive task or waiting for a sql query to be finished. I suggest to open two sessions (just open http://127.0.0.1:14072/ in two tabs) and play with the buttons to test the functionality.
run_app.R:
library(shiny)
library(future)
library(shinyjs)
runApp(host = "127.0.0.1", port = 14072, launch.browser = TRUE)
ui.R:
ui <- fluidPage(
useShinyjs(),
textOutput("existsFutureData"),
numericInput("duration", "Duration", value = 5, min = 0),
actionButton("start_proc", h5("get data")),
actionButton("start_proc_future", h5("get data using future")),
checkboxInput("checkbox_syssleep", label = "Use Sys.sleep", value = FALSE),
h5('Table data'),
dataTableOutput('tableData'),
h5('Table future data'),
dataTableOutput('tableFutureData')
)
server.R:
plan(multiprocess)
fakeDataProcessing <- function(duration, sys_sleep = FALSE) {
if(sys_sleep) {
Sys.sleep(duration)
} else {
current_time <- Sys.time()
while (current_time + duration > Sys.time()) { }
}
return(data.frame(test = Sys.time()))
}
#fakeDataProcessing(5)
############################ SERVER ############################
server <- function(input, output, session) {
values <- reactiveValues(runFutureData = FALSE, futureDataLoaded = 0L)
future.env <- new.env()
output$existsFutureData <- renderText({ paste0("exists(futureData): ", exists("futureData", envir = future.env)," | futureDataLoaded: ", values$futureDataLoaded) })
get_data <- reactive({
if (input$start_proc > 0) {
shinyjs::disable("start_proc")
isolate({ data <- fakeDataProcessing(input$duration) })
shinyjs::enable("start_proc")
data
}
})
observeEvent(input$start_proc_future, {
shinyjs::disable("start_proc_future")
duration <- input$duration # This variable needs to be created for use in future object. When using fakeDataProcessing(input$duration) an error occurs: 'Warning: Error in : Operation not allowed without an active reactive context.'
checkbox_syssleep <- input$checkbox_syssleep
future.env$futureData %<-% fakeDataProcessing(duration, sys_sleep = checkbox_syssleep)
future.env$futureDataObj <- futureOf(future.env$futureData)
values$runFutureData <- TRUE
check_if_future_data_is_loaded$resume()
},
ignoreNULL = TRUE,
ignoreInit = TRUE
)
check_if_future_data_is_loaded <- observe({
invalidateLater(1000)
if (resolved(future.env$futureDataObj)) {
check_if_future_data_is_loaded$suspend()
values$futureDataLoaded <- values$futureDataLoaded + 1L
values$runFutureData <- FALSE
shinyjs::enable("start_proc_future")
}
}, suspended = TRUE)
get_futureData <- reactive({ if(values$futureDataLoaded > 0) future.env$futureData })
output$tableData <- renderDataTable(get_data())
output$tableFutureData <- renderDataTable(get_futureData())
session$onSessionEnded(function() {
check_if_future_data_is_loaded$suspend()
})
}
I retooled André le Blond's excellent answer to and made a gist showing a generic asynchronous task processor which can be used either by itself or with Shiny: FutureTaskProcessor.R
Note it contains two files: FutureProcessor.R which is the stand alone asynchronous task handler and app.R which is a Shiny App showing use of the async handler within Shiny.
One admittedly complicated workaround to the single-threaded nature of R within Shiny apps is to do the following:
Splinter off an external R process (run another R script located in
the Shiny app directory, or any directory accessible from within the
Shiny session) from within R (I've tried this splintering before,
and it works).
Configure that script to output its results to a temp directory (assuming you're running Shiny on a Unix-based system) and give the output file a unique filename (preferably named within the namespace of the current session (i.e. "/tmp/[SHINY SESSION HASH ID]_example_output_file.RData".
Use Shiny's invalidateLater() function to check for the presence of that output file.
Load the output file into the Shiny session workspace.
Finally, trash collect by deleting the generated output file after loading.
I hope this helps.

Resources