rexyai / RestRserve

R web API framework for building high-performance microservices and app backends
https://restrserve.org
271 stars 31 forks source link

Execution halted and endpoint stop working when saveWidget() is used #174

Closed duribef closed 3 years ago

duribef commented 3 years ago

Hi all,

I’m using RestRServe to enable an API that executes certain functions including HTML like leaflets and highcharts following the example at https://github.com/rexyai/RestRserve/blob/master/inst/examples/plot-leaflet/app.R. However, when any of these endpoints is called more than once in a short time period on localhost (double clicks), the following error appears related to the path generated by saveWidget()

Error in (function (..., config.file = "/etc/Rserve.conf") : ignoring SIGPIPE signal Calls: sourceWithProgress ... eval -> eval -> <Anonymous> -> do.call -> <Anonymous> Execution halted {"timestamp":"2021-01-22 16:08:57.014399","level":"ERROR","name":"Application","pid":64379,"msg":"","context":{"request_id":"46c306b6-5ce5-11eb-899f-d037452a2e54","message":{"error":"path[1]=\"/tmp/Rtmp3Ha2xg\": No such file or directory","call":"normalizePath(dirname(file), mustWork = TRUE)","traceback":["FUN(request, response)","saveWidget(m, tmp)",["pandoc_save_markdown(html, file = file, libdir = libdir, background = background, "," title = title)"],"normalizePath(dirname(file), mustWork = TRUE)"]}}}

Then the error 500 appears and all endpoint that uses saveWidget() stop working. I would like to use this API together with a shiny app, for example with a button to change a date that would change an endopint parameter, so a double click in this button will be fatal. Is there any extra configuration to handle this type of html files and avoid saveWidgets?

For reproducible example https://github.com/rexyai/RestRserve/blob/master/inst/examples/plot-leaflet/app.R

Best regards !

R version 4.0.3 (2020-10-10)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 20.04.1 LTS

Matrix products: default
BLAS:   /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.9.0
LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.9.0

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
 [3] LC_TIME=es_CL.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=es_CL.UTF-8    LC_MESSAGES=en_US.UTF-8   
 [7] LC_PAPER=es_CL.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=es_CL.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] grid      parallel  stats     graphics  grDevices utils    
[7] datasets  methods   base     

other attached packages:
 [1] googleAuthR_1.3.1         googleCloudStorageR_0.5.1
 [3] bigQueryR_0.5.0           DBI_1.1.0                
 [5] bigrquery_1.3.2           mongolite_2.2.1          
 [7] reshape2_1.4.4            raster_3.4-5             
 [9] webshot_0.5.2             rgdal_1.5-18             
[11] sp_1.4-4                  lubridate_1.7.9.2        
[13] ggplot2_3.3.2             jpeg_0.1-8.1             
[15] gridExtra_2.3             tidyr_1.1.2              
[17] highcharter_0.8.2         doParallel_1.0.16        
[19] iterators_1.0.13          foreach_1.5.1            
[21] purrr_0.3.4               magick_2.6.0             
[23] pals_1.6                  dplyr_1.0.2              
[25] htmlwidgets_1.5.3         leaflet_2.0.4.1          
[27] RestRserve_0.4.0          jsonlite_1.7.2           
[29] httr_1.4.2               

loaded via a namespace (and not attached):
 [1] fs_1.5.0          xts_0.12.1        bit64_4.0.5      
 [4] tools_4.0.3       backports_1.2.1   R6_2.5.0         
 [7] colorspace_2.0-0  withr_2.3.0       tidyselect_1.1.0 
[10] bit_4.0.4         curl_4.3          compiler_4.0.3   
[13] cli_2.2.0         scales_1.1.1      checkmate_2.0.0  
[16] askpass_1.1       stringr_1.4.0     digest_0.6.27    
[19] rmarkdown_2.6     dichromat_2.0-0   pkgconfig_2.0.3  
[22] htmltools_0.5.0   dbplyr_2.0.0      maps_3.3.0       
[25] rlang_0.4.9       TTR_0.24.2        rstudioapi_0.13  
[28] quantmod_0.4.18   generics_0.1.0    farver_2.0.3     
[31] zoo_1.8-8         crosstalk_1.1.0.1 zip_2.1.1        
[34] magrittr_2.0.1    rlist_0.4.6.1     feather_0.3.5    
[37] fansi_0.4.1       Rcpp_1.0.5        munsell_0.5.0    
[40] lifecycle_0.2.0   stringi_1.5.3     yaml_2.2.1       
[43] plyr_1.8.6        crayon_1.3.4      lattice_0.20-41  
[46] mapproj_1.2.7     hms_0.5.3         knitr_1.30       
[49] pillar_1.4.7      igraph_1.2.6      uuid_0.1-4       
[52] codetools_0.2-18  XML_3.99-0.5      glue_1.4.2       
[55] beepr_1.3         evaluate_0.14     data.table_1.13.4
[58] vctrs_0.3.6       gtable_0.3.0      openssl_1.4.3    
[61] assertthat_0.2.1  xfun_0.19         mime_0.9         
[64] broom_0.7.3       ncdf4_1.17        gargle_0.5.0     
[67] audio_0.1-7       tibble_3.0.4      memoise_1.1.0    
[70] ellipsis_0.3.1  
dselivanov commented 3 years ago

I think what happens here is tmp dir where you save widgets (/tmp/Rtmp3Ha2xg in the example) is getting deleted by system (or overwritten by another request handler call?).

What I would do - I would generate html on the fly (for example using htmltools::htmlTemplate and htmltools::renderDocument) and return them as a string with proper content type ('text/html').

dselivanov commented 3 years ago

closing as not active

richarddmorey commented 1 year ago

I am seeing this error as well. I'm trying to get a minimum reproducible example, but the problem is I can't reproduce it reliably. I have an app that writes some HTML to a folder (created with tempdir(), eg, /blah/blah/.../Rtmpp5RZDO), then uses RestRServe to send it to the client. The temp folder is also used to cache using {cachem}'s disk caching functions. Sometimes - but only rarely - the whole temp folder, at the root, simply disappears. The cache is gone (a subfolder), the subfolder where the HTML files are saved: all of it.

Could a child process quitting cause R to clean up the tempdir (eg, https://stat.ethz.ch/pipermail/r-devel/2017-February/073748.html)? I'm at my wit's end trying to debug this, due to the fact I can't reliably reproduce it. It just seems to happen randomly. Any tips to help me work out the problem with be appreciated.

s-u commented 1 year ago

The R temporary directory is not guaranteed to exist beyond the duration of the call, so it cannot be used to keep any content around that should be available after the request finished. If you need to store something, you should use either app directory or app-specific temporary directory (not inside R's temporary space). As Dmitry pointed out, in most cases you want to avoid that and return the full html if you can. In practice the temporary directory may live longer as it's typically kept for the duration of the connection, but that's not guaranteed.

(BTW the original example is gone so it you have a reproducible example, please post it here).

Note that lifetime management of results is something you may have to think about a bit. Cleanup is a bit tricky, as you need to know whether the files you created may be needed later or not. How long do you want to guarantee the result of the next GET request? Do you wants to require DELETE request or do some time-based cleanup? You also don't want them to sit around forever. Probably the easiest is to create a tmp directory under the app root and clean it up at each start or possibly periodically.

dselivanov commented 1 year ago

@richarddmorey

richarddmorey commented 1 year ago

Thanks both for the advice. I wish I had a reproducible example, but it seems to happen randomly. I had assumed that the tempdir() should be around for the whole R session, but it keeps disappearing. So moving the {cachem} dick cache to another place and use a more premanent location for writing (and cleaning up after) is the best option.

s-u commented 1 year ago

@richarddmorey again, the code you use would be crucial here. The tempdir is around for a session, but for a request a session is only as long as the request, so you'd have to clarify which session you are talking about.

One common mistake is to re-use temporary directories between sessions which is not supported, because R will clean it up at exit, so if you share it another session may blow away the contents of a session that is still running. However, without an example I can't look into it.

richarddmorey commented 1 year ago

@s-u - Unfortunately I can't get it to reproduce reliably, so I can't strip it down to make it minimal. I just know that somehow the tempdir() sometimes gets deleted while RestRserve is running, destroying the cache and interfering with rmarkdown compilation (ie, it can happen between copying files to a temporary subfolder and rendering those files). It's been difficult to debug due to the unreliability.

I'm going to work around it for now using directories that R won't delete in place of tempdir(), and I'll come back with the best example I can (if necessary). I'll open a new issue in that case.