Update: I added the solution below
The problem
A little background: I am running Shiny server v1.5 on an Ubuntu precise server. Basically I have an rmarkdown page that allows the user to play with some parameters and then has a download button that generates a document using shiny like so:
```{r,echo=FALSE}
downloadHandler(
filename = "report.pdf",
content = function(file) {
knitr::knit2pdf("report.Rnw",envir=environment())
file.rename(normalizePath('report.pdf'), file)
}
)
```
The Shiny application works fine on my local machine, but crashes on the server. More specifically, the r markdown webpage works fine, but once I click the download button, the app crashes after about 30 seconds. The server logs indicate a socket time-out error:
[2016-02-24 03:38:52.240] [INFO] shiny-server - Starting listener on 0.0.0.0:3838
[2016-02-24 03:49:30.183] [ERROR] shiny-server - Uncaught exception: Error: socket hang up
[2016-02-24 03:49:30.184] [ERROR] shiny-server - Error: socket hang up
at createHangUpError (_http_client.js:215:15)
at Socket.socketCloseListener (_http_client.js:247:23)
at Socket.emit (events.js:129:20)
at TCP.close (net.js:485:12)
[2016-02-24 03:49:30.184] [INFO] shiny-server - Stopping listener on 0.0.0.0:3838
[2016-02-24 03:49:30.185] [INFO] shiny-server - Shutting down worker processes (with notification)
/opt/shiny-server/lib/main.js:364
throw err;
^
Error: socket hang up
at createHangUpError (_http_client.js:215:15)
at Socket.socketCloseListener (_http_client.js:247:23)
at Socket.emit (events.js:129:20)
at TCP.close (net.js:485:12)
This pdf takes quite a long time to generate (about five minutes), so I'm suspecting that I missed some time-out parameter somewhere. This is what I did in my shiny configuration:
run_as shiny;
app_init_timeout 999999;
app_idle_timeout 999999;
# Define a server that listens on port 3838
server {
listen 3838;
....
}
... but to no avail, because I'm still getting the error. Any suggestions would be greatly appreciated!
The solution
As mentioned by #daattali, you cannot have something take that long inside downloadButton, changing the server settings will not change anything about that. So I ended up splitting the generation & download functionality in two parts like so:
```{r, echo=FALSE}
shinyApp(
ui = fluidPage(
fluidRow(
column(2,
conditionalPanel(
condition = "!$('makeReport').hasClass('shiny-busy')",
actionButton("makeReport","Generate Report",icon=icon("file"))
)
),
column(4,
conditionalPanel(
condition = "!$('makeReport').hasClass('shiny-busy')",
uiOutput("downloadButton")
#downloadButton("downloadReport", "Download Report")
)
)
)
),
server = function(input, output) {
output$download_button <- renderUI({
downloadButton("downloadReport", "Download Results")
})
makeReportAction <- eventReactive(input$makeReport, {
...
knitr::knit2pdf("report.Rnw",envir=globalenv())
})
output$downloadButton <- renderUI({
makeReportAction() #only appear after first click on generate
downloadButton("downloadReport", "Download Report")
})
output$downloadReport <- downloadHandler(
filename = "report.pdf",
content = function(file) {
cat(paste("Does the pdf exist?",file.exists("report.pdf")))
file.rename(normalizePath('report.pdf'), file)
}
)
}
)
```
It might be the same problem someone else asked Joe Cheng about in the Google board last week
https://groups.google.com/forum/#!topic/shiny-discuss/4bL9jFaYly0
It looks like there's a time limit on the download handler, so maybe try seeing if it's possible to generate the file beforehand and only serving the file when the button is clicked
Related
I am getting an error
The application failed to start: exited unexpectedly with code 1
Error in enforcePackage(name, curVersion) :
The shiny package was not found in the library.
Calls: local ... eval -> eval -> eval -> -> enforcePackage
Execution halted
when I try to deploy shiny app. I found similar problem that claimed that having library(shiny) solves it, it didn't help. I also tried lib=("path/to/shiny"), no effect.
Here is my code
library(shiny)
library(DT)
library(rmarkdown)
###
ui <- fluidPage(fluidRow(column(10, div(dataTableOutput("dataTable")))))
server <- function(input, output, session) {
data <- reactiveFileReader(100, session, 'excel.csv', read.csv)
output$dataTable <- renderDT(
data(),
class = "display nowrap compact",
filter = "top",
options = list(
scrollX = TRUE,
searchCols = default_search_columns,
search = list(regex = FALSE, caseInsensitive = FALSE, search = default_search)
)
)
}
shinyApp(ui, server)
rsconnect::deployApp('C:/path//app1.Rmd')
Any help is appreciated.
The error indicates that Shiny is not installed in the environment you are deploying to.
If deploying to a server you own, first install Shiny Server, and confirm that the examples work as expected.
Then consult with the Administrator's Guide to set it up as you like it.
Here is my code for a simple test app:
library(shiny)
library(shinydashboard)
library(googlesheets4)
library(googledrive)
library(DT)
drive_auth(email = "xxxx")
shinyws1<-gs4_create("WS1")
#table<-read_sheet("xxx")
# Define UI for application
ui <- fluidPage(
# Application title
titlePanel("Test App"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
numericInput("bins",
"Number of friends:",
min = 1,
max = 100,
value = 50),
actionButton("submit","Submit",class="btn-success")
),
# Show a plot of the generated distribution
mainPanel(
#blank so far
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output) {
#results<-reactive(input$bins)
observeEvent(input$submit,{
shinyws1 %>% sheet_append(as.data.frame(input$bins))
})
}
# Run the application
shinyApp(ui = ui, server = server)
It works okay in my local server. But deployment fails.
Here is the error message generated after deployment in Shinyio server:
Error in value[[3L]](cond) : Can't get Google credentials.
Are you running googledrive in a non-interactive session? Consider:
* `drive_deauth()` to prevent the attempt to get credentials.
* Call `drive_auth()` directly with all necessary specifics.
* Read more in: https://gargle.r-lib.org/articles/non-interactive-auth.html
Calls: local ... tryCatch -> tryCatchList -> tryCatchOne -> <Anonymous>
Execution halted
Does anyone know how to fix it? I've tried every workaround I found online, but it didn't work.
I'm posting this because I have not managed to get the solutions posted other places working. I'm trying to re-deploy a shiny dash, but it is failing to install a package at deploy.
It's the BioConductor error, but the package it claims to fail for is a CRAN package, and so I have no idea what to do.
MRE:
library(ggseg); library(shiny); library(tidyverse); library(plotly)
# Define UI ----
ui <- fluidPage(
# Application title
titlePanel("Demonstration of ggseg package"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
radioButtons(inputId = "atlasChoice", label="Choose atlas",
choiceValues = c("dkt_3d","yeo7_3d",),
choiceNames = c("DKT", "Yeo7"),
inline = FALSE, width = NULL),
radioButtons(inputId = "positionChoice", label="Choose position",
choices = c("LCBC left","LCBC right"),
inline = FALSE, width = NULL)
),
# Show a plot of the generated distribution
mainPanel(
uiOutput("plotUI")
)
)
)
# Define server ----
server <- function(input, output) {
output$plotUI <- renderUI({
plotlyOutput("plotlyPlot")
})
output$plotlyPlot <- renderPlotly({
cc = strsplit(input$positionChoice, " ")[[1]]
ggseg3d(atlas=input$atlasChoice,
surface=cc[1],
hemisphere=cc[2]
)
})
}
# Run the application
shinyApp(ui = ui, server = server)
My repos are set as so:
getOption("repos")
BioCsoft
"https://bioconductor.org/packages/3.7/bioc"
BioCann
"https://bioconductor.org/packages/3.7/data/annotation"
BioCexp
"https://bioconductor.org/packages/3.7/data/experiment"
BioCworkflows
"https://bioconductor.org/packages/3.7/workflows"
CRAN
"https://cran.rstudio.com"
And the error is as following:
Preparing to deploy document...DONE
Uploading bundle for document: 619289...DONE
Deploying bundle: 1770029 for document: 619289 ...
Waiting for task: 573690766
building: Parsing manifest
################################ Begin Task Log ################################
################################# End Task Log #################################
Error: Unhandled Exception: Child Task 573690767 failed:
Error parsing manifest: Unable to determine package source for Bioconductor package oompaBase: Repository must be specified
Execution halted
I don't know if Athanasia's solved this in the end, but I had a similar problem today, so here's what worked for me, in case it's useful for someone else :)
My app uses biomaRt, which I think depends on Biobase. When I tried to deploy, I had the error:
Error: Unhandled Exception: Child Task 601909864 failed: Error parsing manifest: Unable to
determine package source for Bioconductor package Biobase: Repository must be specified
I changed my repos settings based on instructions I found here. This alone also didn't work for me.
Once I reinstalled Biomart using BiocInstaller::biocLite(), my app deployed successfully :)
server <- function(input, output, session) {
out1_rows <- reactiveVal()
observeEvent(input$run1, {
prog <- Progress$new(session)
prog$set(message = "Analysis in progress",
detail = "This may take a while...",
value = NULL)
fut1 = future({
system(paste("Command1" , input$file ">", "out1.txt"))
system(paste("Command2" , out1.txt ">", "out2.txt"))
head_rows <- read.delim("out2.txt")
return(head_rows)
}) %...>%
out1_rows() %>%
finally( ~ prog$close())
NULL
})
observeEvent(req(out1_rows()), {
output$out_table <-
DT::renderDataTable(DT::datatable(
out1_rows(),
)
))
observeEvent(input$cancel, {
async_pid <- fut1$job$pid ##this is empty
#async_pid <- Sys.getpid() ##this return PID for main process and kills "/opt/shiny-server/R/SockJSAdapter.R" but not for subprocesses inside future()
system(paste("kill -15", async_pid))
})
}
Here, i would need to kill the process running the commands inside future(). I tried in the above way to fetch the PID running the future() process and kill when input$cancel is triggered. However, fut1$job$pid is not returning any PID value and hence the kill operation is not successful.
This link from future vignettes shows how to fetch PID for future() jobs. However, in my case i am not able to use Sys.getpid() inside future() as i am not sure how to store the PID value as the process is already returning some output from my system commands.
This page future GIT shows an alternate way of External Kill with the syntax fut1$job$pid. But this fails to fetch the PID.
I couldn't figure this out after trying different ways or blinded with the syntax. Could someone hint the way to do this.
Can you please provide us with a full reproducible example?
You might want to have a look at library(future.callr):
using plan(callr) you can get the pid and kill the process like this:
library(future)
library(promises)
library(future.callr)
plan(callr)
myFuture <- future({
Sys.sleep(5)
return(runif(1))
})
myFuture$process$get_pid()
myFuture$process$is_alive()
# myFuture$process$kill()
# myFuture$process$is_alive()
then(myFuture, onFulfilled = function(value){
print(value)
}, onRejected = NULL)
Edit - adapted from your code:
library(shiny)
library(DT)
library(future)
library(promises)
library(future.callr)
library(shinyjs)
library(V8)
plan(callr)
ui <- fluidPage(
useShinyjs(),
titlePanel("Trigger & kill future"),
sidebarLayout(
sidebarPanel(
actionButton(inputId="run1", label="run future"),
actionButton(inputId="cancel", label="kill future")
),
mainPanel(
dataTableOutput('out_table')
)
)
)
server <- function(input, output, session) {
disable("cancel")
out1 <- reactiveValues(rows=NULL)
observeEvent(input$run1, {
disable("run1")
enable("cancel")
out1$rows <- NULL
prog <- Progress$new(session)
prog$set(message = "Analysis in progress",
detail = "This may take a while...",
value = NULL)
fut1 <<- future({
# system(paste("Command1" , input$file, ">", "out1.txt"))
# system(paste("Command2" , out1.txt, ">", "out2.txt"))
# head_rows <- read.delim("out2.txt")
head_rows <- data.frame(replicate(5, sample(runif(20, 0, 1), 20, rep=TRUE)))
Sys.sleep(5)
return(head_rows)
})
print(paste("Running async process with PID:", fut1$process$get_pid()))
then(fut1, onFulfilled = function(value){
out1$rows <<- value
}, onRejected = function(error){NULL})
finally(fut1, function(){
prog$close()
disable("cancel")
enable("run1")
})
return(NULL)
}, ignoreInit = TRUE)
observeEvent(req(out1$rows), {
output$out_table <- DT::renderDataTable(DT::datatable(out1$rows))
})
observeEvent(input$cancel, {
async_pid <- fut1$process$get_pid()
print(paste("Killing PID:", async_pid))
# system(paste("kill -9", async_pid)) # Linux - kill
# system(paste("taskkill /f /pid", async_pid)) # Windows - kill
fut1$process$kill() # library(future.callr) - kill
out1$rows <- NULL
disable("cancel")
enable("run1")
}, ignoreInit = TRUE)
}
shinyApp(ui = ui, server = server)
Errors occuring occasionally:
Unhandled promise error: callr failed, could not start R, exited with non-zero status, has crashed or was killed
Warning: Error in : callr failed, could not start R, exited with non-zero status, has crashed or was killed
95: <Anonymous>
Maybe this statement from #HenrikB tells us what we are running into:
However, to get this working properly you probably also need to make
your future expression / future code interrupt aware using
withCallingHandlers() etc. It'll also unknown to me what happens if
you signal too many interrupts in a row - it might be that you manage
to interrupt the main R-loop of the worker, which then will cause the
R worker to terminate. That'll result in a missing R worker and you've
got that problem you mention at the beginning.
The error is also mentioned here but currently in the future.callr-context I don't know how to work around it.
2nd Edit:
By now I got some further feedback from Henrik Bengtsson. He once again mentions that the core future API currently isn't supporting the termination of futures. So, in the end no matter what backend we use, we might run into problems.
Disregarding this info, I'd have another look at the library(ipc) vignette which provides two examples on the topic Killing a long running process. But I pointed to this here already - which probably led to this question.
In the end all of this might be useless regarding your scenario because you are using system() calls which create subprocesses of their own (and have their own pid accordingly). Therefore why don't you use wait = FALSE in your system command (as I mentioned in the comments here already) to get your async behaviour and catch their pid using something like myCommand & echo $! (see this). This way you don't need any futures.
Could find the PID by insetring cat() as such:
future({
cat('future process PID \n', Sys.getpid(), '\')
<expensive operation>
})
the PID number will appear in console when running the app. This is how I have found out that, in my case, the PID outside and inside the future were identical.
I made a portable shiny app with portable chrome and R. I launch rscript with vbs script in which stdout is redirected to a logfile with:
RScriptFile & " 1> " & logfile & " 2>&1"
What I want to do is to read in the log file so that the user has feedback about the calculation progress. The following reactivePoll works if the "main" shiny script is writing to the console, but it does not if a sourced function writes. Note that both are displayed in the log file, therefore the fault is surely not in the redirection, but somewhere in my logic or in shiny.
If I set bool_for_testing = T the test button works, on the other hand if I set it False not even the first "Go!" is printed on the console until the script is finished, but both are displayed real time in the logfile:
library(shiny)
bool_for_testing = F # note its use below!
logfile_name = "some_log_file_that_exists" # this is created by the vbs (command line) script!
ui = shinyUI(fluidPage(
actionButton("btn_test", "push meh!"),
fluidRow(
column(12, wellPanel(style = "overflow-y:scroll; max-height: 600px",
tags$b(paste("log file is this one:", logfile_name)),
verbatimTextOutput("logText")
))
)
)
) # end UI
server = shinyServer(function(input, output, session) {
observe({
if(input$btn_test > 0){
if(bool_for_testing) {
# this works!
print("Go!")
} else {
# not a letter on log here
print("Go!")
source("some_barely_interesting_function.R")
some_barely_interesting_function()
}
}
})
logFile <- reactivePoll(1000, session,
checkFunc = function() {
if (file.exists(logfile_name))
# should always refresh
runif(1) # Sys.time() does not work either
else
""
},
valueFunc = function() {
readLines(logfile_name)
}
)
# # this does not work either
# fileReaderData <- reactiveFileReader(500, session,
# logfile_name, readLines)
output$logText <- renderText({
text <- logFile()
paste(text, collapse = '\n')
})
session$onSessionEnded(function() {
# for portable R and chrome application
stopApp()
q("no")
})
})
shinyApp(ui = ui, server = server)
If I manually open the log file it shows the progress continuously, therefore I think the GUI thread is held up until the sourced calculation is finished (if I open a new shiny window only for reading the log file, it will work well, too). Is this truly the case, and is there a workaround for this?