Reputation: 28675
I have the Shiny app below. The first time I select any given number it takes 3 seconds to load the result. Due to bindCache
, I get the result instantly if I select the same number later.
However, I don't want to select all 10 numbers manually just to make my app responsive before I present it. Is there any way to cache a set of inputs in advance? In this example I'd want to cache the results for input$num
values 1 through 10. In the real app there's approximately 5 inputs each with 5 possible values for 25 possible results I'd want to cache.
library(shiny)
ui <- fluidPage(
sliderInput('num', 'Pick a number:', min = 1, max = 10, value = 1),
textOutput('out')
)
server <- function(input, output, session) {
output$out <- reactive({
Sys.sleep(3)
paste("Your number is:", input$num)
}) %>% bindCache(input$num)
}
shinyApp(ui, server)
Note:
One response might be that I should manually pre-compute these results. In the real app most of the time comes from gt::render_gt
and gt::gt_output
on gt
tables that I've created in advance. I believe these functions can only be used in a reactive context (meaning only in a Shiny app?)
Edit:
As a side-note, my original problem was solved by using gt::as_raw_html
to render the tables in a step before the shiny app. Still leaving the question though, since it's sometimes a problem in other situations.
Upvotes: 5
Views: 1163
Reputation: 41
Another idea, when you need to pre-cache output, especially plots, is to automatically simulate the activity of FIRST user (on whom the cache is created) every time the initial data changes. So the real users will interact with already cached output. I got the solution from the video of Joe Cheng, thanks to user Pedro Faria for the link.
devtools::install_github('rstudio/shinyloadtest')
library(shinyloadtest)
shinyloadtest::record_session("http://shinyapp.address:3838",host="127.0.0.1",port
= 8600, open_browser = TRUE)
record_session() will open a browser displaying the app. You do all user activity, then close the browser. After closing, a file (recording.log by default), that contains a recording of the session, will be created in the R user directory. ( use getwd() to know)
Command to use in system’s terminal:
shinycannon /path/to/recording.log http://localhost:3838 --workers 1 --loaded-duration-minutes 1
Shinycannon is able to run on whatever platforms Java virtual machine is. For example in Armbian, after building packages, terminal command will look like:
java -jar /path/to/shinycannon-1.1.3.9000-8e10a8e.jar /path/to/recording.log http://localhost:3838 --workers 1 --loaded-duration-minutes 1
Upvotes: 4
Reputation: 20329
How about using a persistent cache, running the app once, where you manually change all inputs as needed (I also included an automated version, which I am not totally happy with, b/c race conditions could occur) and then in subsequent runs you have all the values properly cached?
library(shiny)
library(magrittr)
## change path to a non temp diretcory to keep that even after reboot
shinyOptions(cache = cachem::cache_disk(file.path(dirname(tempdir()),
"myapp-cache")))
xl <- 1:3
yl <- 1:3
ui <- fluidPage(
sliderInput("x", "x", min(xl), max(xl), min(xl), 1),
sliderInput("y", "y", min(yl), max(yl), min(yl), 1),
verbatimTextOutput("z"),
actionButton("fill", "Fill Cache")
)
server <- function(input, output, session) {
idx <- idy <- 1
r <- reactive({
message("Doing expensive computation...")
Sys.sleep(2) ## simulate expensive op
input$x + input$y
}) %>% bindCache(input$x, input$y)
observe({
req(input$fill)
if (idx != length(xl) + 1 || idy != length(yl)) {
## need the invalidateLater approach
## to allow shiny reacting on the change
## not sure whether we cannot trip over race conditions
## recommendation: do it once by hand (it's persistent anyways ;)
invalidateLater(500, session)
if (idx == length(xl) + 1) {
message("Updating y:", idy)
idx <<- 1
idy <<- idy + 1
updateSliderInput(session, "y", value = yl[[idy]])
} else {
message("Updating x:", idx)
updateSliderInput(session, "x", value = xl[[idx]])
idx <<- idx + 1
}
}
})
output$z <- renderText(r())
}
## Start app and set all values
shinyApp(ui, server)
## Close app and restart
## Cache is now filled
shinyApp(ui, server)
Upvotes: 6