plumber icon indicating copy to clipboard operation
plumber copied to clipboard

plumber not releasing memory

Open LukasK13 opened this issue 4 years ago • 21 comments

System details

Output of sessioninfo::session_info():

R version 3.6.0 (2019-04-26)
Platform: x86_64-redhat-linux-gnu (64-bit)
Running under: CentOS Linux 7 (Core)

Matrix products: default
BLAS/LAPACK: /usr/lib64/R/lib/libRblas.so

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

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

other attached packages:
 [1] urltools_1.7.3          httr_1.4.0              lobstr_1.0.1            stringr_1.4.0           yaml_2.2.0              xml2_1.2.0             
 [7] SweetAleRt_1.0          SummeRnote_1.0          shinyWidgets_0.4.8      shinyjs_1.0             shinydashboard_0.7.1    shinyBS_0.61           
[13] sharepointr_1.0.0       RMySQL_0.10.17          rintrojs_0.2.1          RColorBrewer_1.1-2      promises_1.0.1          plotly_4.9.0           
[19] openssl_1.3             officer_0.3.4           lubridate_1.7.4         ldapr_0.1.0             Hmisc_4.2-0             Formula_1.2-3          
[25] survival_2.44-1.1       lattice_0.20-38         jsonlite_1.6            gitlabr_1.1.6           magrittr_1.5            ggplot2_3.1.1          
[31] future_1.12.0           fullcalendaR_1.0        dtplyr_0.0.3            DT_2.0.0                dplyr_0.8.1             directlabels_2018.05.22
[37] dbplyr_1.4.0            DBI_1.0.0               data.table_1.12.4       colourpicker_1.0        bcrypt_1.1              shiny_1.3.2            

loaded via a namespace (and not attached):
 [1] tools_3.6.0         backports_1.1.4     R6_2.4.0            rpart_4.1-15        lazyeval_0.2.2      colorspace_1.4-1    nnet_7.3-12        
 [8] withr_2.1.2         tidyselect_0.2.5    gridExtra_2.3       curl_3.3            compiler_3.6.0      htmlTable_1.13.1    triebeard_0.3.0    
[15] labeling_0.3        scales_1.0.0        checkmate_1.9.3     quadprog_1.5-7      askpass_1.1         plumber_0.4.6       digest_0.6.19      
[22] foreign_0.8-71      base64enc_0.1-3     pkgconfig_2.0.2     htmltools_0.3.6     htmlwidgets_1.3     rlang_0.3.4         rstudioapi_0.10    
[29] crosstalk_1.0.0     acepack_1.4.1       zip_2.0.2           Matrix_1.2-17       Rcpp_1.0.1          munsell_0.5.0       arpr_0.1.1         
[36] stringi_1.4.3       plyr_1.8.4          grid_3.6.0          listenv_0.7.0       crayon_1.3.4        miniUI_0.1.1.1      splines_3.6.0      
[43] knitr_1.22          pillar_1.4.0        uuid_0.1-2          codetools_0.2-16    glue_1.3.1          V8_2.2              latticeExtra_0.6-28
[50] httpuv_1.5.1        gtable_0.3.0        purrr_0.3.2         tidyr_0.8.3         assertthat_0.2.1    xfun_0.7            mime_0.6           
[57] xtable_1.8-4        later_0.8.0         viridisLite_0.3.0   tibble_2.1.1        cluster_2.0.8       globals_0.12.4    

Example application or steps to reproduce the problem

#* @get /getData
function(n = 10000) {
  data=do.call(rbind, replicate(n, mtcars, simplify = F))
  return(data)
}

Describe the problem in detail

Requesting large datasets via plumber results in a large memory consumption of the plumber process which is ok. After the request ist finished, this memory is not released by the garbage collection. This means, if 4 GB were allocated for serving the request, the process will keep the allocated memory forever until restarted. Even after requesting a small dataset after the large one, the memory won't be released. I'm not quite sure if this is actually a bug or if I am doing something wrong.

Thanks for your help!

LukasK13 avatar Oct 09 '19 14:10 LukasK13

I think I experienced something similar before but never had a chance to take a deep look at it... The memory is not increased infinitely but is larger than my expectation. However, since I use plumber on Docker containers, I'm not sure it's because the docker container does not collect the memory in time or an issue of plumber...

shrektan avatar Oct 09 '19 17:10 shrektan

I am able to reproduce this as well. It is not clear where it is happening. Profvis shows a minor mem leak compared to the process memory increase. @wch and I will be investigating.

Even using the route below causes the mem leak. So it's not caused by creating the data. Seems to be when transferring the data.

big_dt <- dplyr::bind_rows(replicate(10000, mtcars, simplify = F))
#* @get /getData3
function() {
  return(big_dt)
}

schloerke avatar Oct 09 '19 17:10 schloerke

Thank you both for looking into this.

@shrektan Originally, I experienced this using a docker container, but I switched to simple hosting for debugging purposes, where I was able to reproduce the error. So it's definitely a plumber problem.

@schloerke I tried calling gc() in the beginning of each request, so old data from the last request should be removed and memory released. Therefore I made a request for a large dataset in the first place (500 MB) and a small dataset (10 MB) afterwards. I noticed, that some memory was actually released (about 40%) but a significant amount was still allocated. Using gc() in combination with a postserialize hook didn't help either. This tells me, that the there is a scope which aggregates old data without becoming out of scope and that's why it's not affected by calling gc(). What do you think?

Let me know, if I can help finding the cause.

LukasK13 avatar Oct 09 '19 18:10 LukasK13

memleak.png memleak

  • X Axis number of attempts at route
  • Y Axis Memory usage
  • Facets
    • rbind: original /getData where all data is generated, rbind'ed, and returned
    • bind_rows: Similar to the original /getData where all data is generated, dplyr::bind_rows'ed, and returned
    • return: Similar to /bind_rows, but the data is already processed. So only the data is returned.

Thoughts:

  • Each plumber process hovered around 75mb of R objects throughout the whole process. This was checked with pryr::mem_used(). Plumber does not have any compiled code, so this is the number plumber cares about.
  • The R process is gaining overall memory usage. This can be tracked by memuse::Sys.procmem(). I believe this is cause by the R process's cache buffers. Since the leak is not linear and is more asymptotic in shape, this pushes me to believe it is R trying to handle the large influx of memory.
    • This large increase in process memory can be replicated using a for loop and generating the data as /getData does and finally removing the variable. (Without adding plumber in the mix). Even after gc(), the process has over a GB of memory usage.
Final thought

Given that pryr::mem_used() is not increasing during usage and plumber does not have compiled code, I'm taking the stance that plumber is not the culprit. I do not know where to dig further as similar behavior can be reproduced with stock R packages. (Unfortunately, there still is an unexpected increase in memory for the R process.)


I've added a file (and execution output) below to reproduce the whole investigation.

`mem_test.R` file
library(magrittr)

n <- 100
routes <- c("rbind", "bind_rows", "return")

init_file <- function() {
  cat("route, row, mem_used, procmem, time\n", file = "memleak.csv")
}

cat_row <- function(route, i, mem_used, procmem, time) {
  row <- paste(route, i, mem_used, procmem, time, sep = ", ")
  cat(row, "\n", file = "memleak.csv", append = TRUE, sep = "")
}

profile_plumber <- function(route = "getData", port = 12321) {
  pr <- progress::progress_bar$new(
    total = n,
    format = "[:bar] :current/:total eta::eta; :elapsedfull; mem_used: :mem_used; procmem: :procmem\n",
    show_after = 0,
    clear = FALSE
  )
  now <- as.numeric(Sys.time())
  base_url <- paste0("127.0.0.1:", port, "/")
  for (i in 1:n) {
    # ask route
    now <- as.numeric(Sys.time())
    httr::GET(paste0(base_url, route))
    after <- as.numeric(Sys.time())
    # get mem stats
    mem_used <- httr::GET(paste0(base_url, "mem_used")) %>% httr::content() %>% magrittr::extract2(1)
    procmem <- httr::GET(paste0(base_url, "procmem")) %>% httr::content() %>% magrittr::extract2(1)
    pr$tick(tokens = list(
      mem_used = mem_used,
      procmem = procmem
    ))
    # save
    cat_row(route, i, mem_used, procmem, after - now)
  }
}

profile_plot <- function() {
  library(ggplot2)
  readr::read_csv("memleak.csv") %>%
    dplyr::select(-time) %>%
    tidyr::pivot_longer(names_to = "fn", values_to = "memory", c("mem_used", "procmem")) %>%
    dplyr::mutate(
      route = factor(route, levels = routes),
      memory = sub(" M[i]*B", "", memory) %>% as.numeric()
    ) %>%
    {
      dt <- .
      ggplot(dt, aes(row, memory, color = fn)) +
        geom_line() +
        facet_grid(. ~ route) +
        labs(y = "memory (MiB)")
    } %>%
    ggsave(filename = paste0("memleak.png"), width = 10, height = 6)
}


cat(file = "plumber.R",
'#* @get /rbind
function(n = 10000) {
  data=do.call(rbind, replicate(n, mtcars, simplify = F))
  return(data)
}
#* @get /bind_rows
function(n = 10000) {
  dt <- replicate(n, mtcars, simplify = F)
  data <- dplyr::bind_rows(dt)
  return(data)
}

big_dt <- dplyr::bind_rows(replicate(10000, mtcars, simplify = F))
#* @get /return
function() {
  return(big_dt)
}

#* @get /procmem
function() {
  # basically calling ps u -p `Sys.getpid()` and getting the rss value
  current_mem <- memuse::swap.unit(memuse::Sys.procmem()[[1]], unit = "MiB")
  size_mem <- memuse::mu.size(current_mem)
  unit_mem <- memuse::mu.unit(current_mem)
  mem <- paste0(round(size_mem, 1), " ", unit_mem)
  return(mem)
}

#* @get /mem_used
function() {
  capture.output({print(pryr::mem_used())})
}
')


port <- httpuv::randomPort()
init_file()
for (route in routes) {
  message("Starting plumber API")
  proc <- callr::r_bg(function(p) {
    plumber::plumb("plumber.R")$run(port = p)
  }, list(p = port))

  message("...sleeping...")
  Sys.sleep(3)

  message("Profiling server: ", route)
  profile_plumber(route, port)

  message("Stopping plumber API")
  proc$kill()

  message("...sleeping...")
  Sys.sleep(3)
}
message("Generate Image")
profile_plot()
Execution output
mem_test: Rscript mem_test.R 
Starting plumber API
...sleeping...
Profiling server: rbind
[------------] 1/100 eta: 1s; 00:00:00; mem_used: 75.8 MB; procmem: 572.5 MiB
[------------] 2/100 eta: 3m; 00:00:03; mem_used: 78.7 MB; procmem: 705.7 MiB
[------------] 3/100 eta: 4m; 00:00:07; mem_used: 78.7 MB; procmem: 778.5 MiB
[>-------------] 4/100 eta: 4m; 00:00:10; mem_used: 78.7 MB; procmem: 848 MiB
[>-----------] 5/100 eta: 5m; 00:00:14; mem_used: 78.7 MB; procmem: 874.4 MiB
[>-------------] 6/100 eta: 5m; 00:00:17; mem_used: 78.7 MB; procmem: 891 MiB
[>-----------] 7/100 eta: 5m; 00:00:21; mem_used: 78.7 MB; procmem: 919.9 MiB
[>-----------] 8/100 eta: 5m; 00:00:25; mem_used: 78.7 MB; procmem: 927.4 MiB
[>-----------] 9/100 eta: 5m; 00:00:28; mem_used: 78.7 MB; procmem: 944.8 MiB
[>----------] 10/100 eta: 5m; 00:00:32; mem_used: 78.7 MB; procmem: 944.5 MiB
[>----------] 11/100 eta: 5m; 00:00:35; mem_used: 78.7 MB; procmem: 957.1 MiB
[>----------] 12/100 eta: 5m; 00:00:39; mem_used: 78.7 MB; procmem: 975.3 MiB
[>---------] 13/100 eta: 5m; 00:00:42; mem_used: 78.7 MB; procmem: 1025.8 MiB
[>---------] 14/100 eta: 5m; 00:00:46; mem_used: 78.7 MB; procmem: 1064.4 MiB
[=>----------] 15/100 eta: 5m; 00:00:50; mem_used: 78.7 MB; procmem: 1064 MiB
[=>--------] 16/100 eta: 5m; 00:00:53; mem_used: 78.7 MB; procmem: 1081.7 MiB
[=>--------] 17/100 eta: 5m; 00:00:57; mem_used: 78.7 MB; procmem: 1099.5 MiB
[=>--------] 18/100 eta: 5m; 00:01:00; mem_used: 78.7 MB; procmem: 1140.4 MiB
[=>--------] 19/100 eta: 5m; 00:01:04; mem_used: 78.7 MB; procmem: 1141.2 MiB
[=>--------] 20/100 eta: 5m; 00:01:08; mem_used: 78.7 MB; procmem: 1145.5 MiB
[=>--------] 21/100 eta: 5m; 00:01:12; mem_used: 78.7 MB; procmem: 1154.2 MiB
[=>--------] 22/100 eta: 4m; 00:01:15; mem_used: 78.7 MB; procmem: 1155.7 MiB
[=>--------] 23/100 eta: 4m; 00:01:19; mem_used: 78.7 MB; procmem: 1147.8 MiB
[==>---------] 24/100 eta: 4m; 00:01:23; mem_used: 78.7 MB; procmem: 1149 MiB
[=>--------] 25/100 eta: 4m; 00:01:26; mem_used: 78.7 MB; procmem: 1152.3 MiB
[==>-------] 26/100 eta: 4m; 00:01:30; mem_used: 78.7 MB; procmem: 1142.7 MiB
[==>-------] 27/100 eta: 4m; 00:01:34; mem_used: 78.7 MB; procmem: 1153.7 MiB
[==>---------] 28/100 eta: 4m; 00:01:38; mem_used: 78.7 MB; procmem: 1168 MiB
[==>-------] 29/100 eta: 4m; 00:01:41; mem_used: 78.7 MB; procmem: 1183.9 MiB
[==>-------] 30/100 eta: 4m; 00:01:45; mem_used: 78.7 MB; procmem: 1193.4 MiB
[==>-------] 31/100 eta: 4m; 00:01:49; mem_used: 78.7 MB; procmem: 1196.4 MiB
[==>-------] 32/100 eta: 4m; 00:01:52; mem_used: 78.7 MB; procmem: 1193.6 MiB
[==>-------] 33/100 eta: 4m; 00:01:56; mem_used: 78.7 MB; procmem: 1199.1 MiB
[==>-------] 34/100 eta: 4m; 00:02:00; mem_used: 78.7 MB; procmem: 1197.7 MiB
[===>------] 35/100 eta: 4m; 00:02:04; mem_used: 78.7 MB; procmem: 1200.4 MiB
[===>------] 36/100 eta: 4m; 00:02:07; mem_used: 78.7 MB; procmem: 1247.1 MiB
[===>------] 37/100 eta: 4m; 00:02:11; mem_used: 78.7 MB; procmem: 1255.6 MiB
[===>------] 38/100 eta: 4m; 00:02:15; mem_used: 78.7 MB; procmem: 1258.1 MiB
[===>------] 39/100 eta: 4m; 00:02:18; mem_used: 78.7 MB; procmem: 1259.7 MiB
[===>------] 40/100 eta: 4m; 00:02:22; mem_used: 78.7 MB; procmem: 1268.7 MiB
[===>------] 41/100 eta: 3m; 00:02:25; mem_used: 78.7 MB; procmem: 1269.5 MiB
[===>------] 42/100 eta: 3m; 00:02:29; mem_used: 78.7 MB; procmem: 1279.8 MiB
[===>------] 43/100 eta: 3m; 00:02:33; mem_used: 78.7 MB; procmem: 1273.6 MiB
[====>-------] 44/100 eta: 3m; 00:02:36; mem_used: 78.7 MB; procmem: 1265 MiB
[===>------] 45/100 eta: 3m; 00:02:40; mem_used: 78.7 MB; procmem: 1283.5 MiB
[=====>------] 46/100 eta: 3m; 00:02:44; mem_used: 78.7 MB; procmem: 1293 MiB
[====>-----] 47/100 eta: 3m; 00:02:48; mem_used: 78.7 MB; procmem: 1295.9 MiB
[====>-----] 48/100 eta: 3m; 00:02:51; mem_used: 78.7 MB; procmem: 1306.7 MiB
[====>-----] 49/100 eta: 3m; 00:02:55; mem_used: 78.7 MB; procmem: 1308.6 MiB
[====>-----] 50/100 eta: 3m; 00:02:59; mem_used: 78.7 MB; procmem: 1301.7 MiB
[====>-----] 51/100 eta: 3m; 00:03:03; mem_used: 78.7 MB; procmem: 1311.9 MiB
[====>-----] 52/100 eta: 3m; 00:03:06; mem_used: 78.7 MB; procmem: 1319.2 MiB
[====>-----] 53/100 eta: 3m; 00:03:10; mem_used: 78.7 MB; procmem: 1295.6 MiB
[====>-----] 54/100 eta: 3m; 00:03:14; mem_used: 78.7 MB; procmem: 1290.3 MiB
[=====>----] 55/100 eta: 3m; 00:03:17; mem_used: 78.7 MB; procmem: 1285.4 MiB
[=====>----] 56/100 eta: 3m; 00:03:21; mem_used: 78.7 MB; procmem: 1297.3 MiB
[=====>----] 57/100 eta: 3m; 00:03:25; mem_used: 78.7 MB; procmem: 1294.6 MiB
[=====>----] 58/100 eta: 3m; 00:03:28; mem_used: 78.7 MB; procmem: 1312.2 MiB
[======>-----] 59/100 eta: 2m; 00:03:32; mem_used: 78.7 MB; procmem: 1300 MiB
[=====>----] 60/100 eta: 2m; 00:03:36; mem_used: 78.7 MB; procmem: 1304.5 MiB
[======>-----] 61/100 eta: 2m; 00:03:40; mem_used: 78.7 MB; procmem: 1307 MiB
[=====>----] 62/100 eta: 2m; 00:03:44; mem_used: 78.7 MB; procmem: 1296.7 MiB
[=====>----] 63/100 eta: 2m; 00:03:47; mem_used: 78.7 MB; procmem: 1290.7 MiB
[=====>----] 64/100 eta: 2m; 00:03:51; mem_used: 78.7 MB; procmem: 1300.5 MiB
[=====>----] 65/100 eta: 2m; 00:03:55; mem_used: 78.7 MB; procmem: 1304.8 MiB
[======>---] 66/100 eta: 2m; 00:03:58; mem_used: 78.7 MB; procmem: 1305.3 MiB
[======>---] 67/100 eta: 2m; 00:04:02; mem_used: 78.7 MB; procmem: 1308.1 MiB
[======>---] 68/100 eta: 2m; 00:04:06; mem_used: 78.7 MB; procmem: 1316.6 MiB
[======>---] 69/100 eta: 2m; 00:04:10; mem_used: 78.7 MB; procmem: 1309.4 MiB
[======>---] 70/100 eta: 2m; 00:04:13; mem_used: 78.7 MB; procmem: 1305.4 MiB
[======>---] 71/100 eta: 2m; 00:04:17; mem_used: 78.7 MB; procmem: 1320.4 MiB
[======>---] 72/100 eta: 2m; 00:04:21; mem_used: 78.7 MB; procmem: 1319.1 MiB
[======>---] 73/100 eta: 2m; 00:04:24; mem_used: 78.7 MB; procmem: 1313.2 MiB
[======>---] 74/100 eta: 2m; 00:04:28; mem_used: 78.7 MB; procmem: 1308.8 MiB
[=======>--] 75/100 eta: 2m; 00:04:32; mem_used: 78.7 MB; procmem: 1317.2 MiB
[=======>--] 76/100 eta: 1m; 00:04:36; mem_used: 78.7 MB; procmem: 1323.8 MiB
[=======>--] 77/100 eta: 1m; 00:04:40; mem_used: 78.7 MB; procmem: 1314.1 MiB
[=======>--] 78/100 eta: 1m; 00:04:44; mem_used: 78.7 MB; procmem: 1308.8 MiB
[=======>--] 79/100 eta: 1m; 00:04:48; mem_used: 78.7 MB; procmem: 1310.6 MiB
[=======>--] 80/100 eta: 1m; 00:04:51; mem_used: 78.7 MB; procmem: 1314.1 MiB
[=========>--] 81/100 eta: 1m; 00:04:55; mem_used: 78.7 MB; procmem: 1321 MiB
[=======>--] 82/100 eta: 1m; 00:04:59; mem_used: 78.7 MB; procmem: 1322.5 MiB
[=======>--] 83/100 eta: 1m; 00:05:02; mem_used: 78.7 MB; procmem: 1320.7 MiB
[=======>--] 84/100 eta: 1m; 00:05:06; mem_used: 78.7 MB; procmem: 1326.6 MiB
[=======>--] 85/100 eta: 1m; 00:05:10; mem_used: 78.7 MB; procmem: 1320.2 MiB
[=========>--] 86/100 eta: 1m; 00:05:14; mem_used: 78.7 MB; procmem: 1314 MiB
[========>-] 87/100 eta:47s; 00:05:17; mem_used: 78.7 MB; procmem: 1323.8 MiB
[========>-] 88/100 eta:44s; 00:05:21; mem_used: 78.7 MB; procmem: 1235.6 MiB
[==========>-] 89/100 eta:40s; 00:05:25; mem_used: 78.7 MB; procmem: 1271 MiB
[========>-] 90/100 eta:37s; 00:05:28; mem_used: 78.7 MB; procmem: 1291.4 MiB
[========>-] 91/100 eta:33s; 00:05:32; mem_used: 78.7 MB; procmem: 1289.4 MiB
[========>-] 92/100 eta:29s; 00:05:36; mem_used: 78.7 MB; procmem: 1298.9 MiB
[========>-] 93/100 eta:26s; 00:05:39; mem_used: 78.7 MB; procmem: 1299.7 MiB
[========>-] 94/100 eta:22s; 00:05:43; mem_used: 78.7 MB; procmem: 1263.5 MiB
[=========>] 95/100 eta:18s; 00:05:47; mem_used: 78.7 MB; procmem: 1285.3 MiB
[=========>] 96/100 eta:15s; 00:05:51; mem_used: 78.7 MB; procmem: 1019.7 MiB
[=========>] 97/100 eta:11s; 00:05:55; mem_used: 78.7 MB; procmem: 1171.1 MiB
[=========>] 98/100 eta: 7s; 00:05:59; mem_used: 78.7 MB; procmem: 1196.3 MiB
[==========>] 99/100 eta: 4s; 00:06:04; mem_used: 78.7 MB; procmem: 933.3 MiB
[=========] 100/100 eta: 0s; 00:06:08; mem_used: 78.7 MB; procmem: 1007.3 MiB

Stopping plumber API
...sleeping...
Starting plumber API
...sleeping...
Profiling server: bind_rows
[------------] 1/100 eta: 0s; 00:00:00; mem_used: 71.9 MB; procmem: 350.2 MiB
[------------] 2/100 eta: 1m; 00:00:01; mem_used: 74.8 MB; procmem: 385.8 MiB
[------------] 3/100 eta: 1m; 00:00:02; mem_used: 74.8 MB; procmem: 408.9 MiB
[------------] 4/100 eta: 1m; 00:00:03; mem_used: 74.8 MB; procmem: 414.7 MiB
[>-----------] 5/100 eta: 1m; 00:00:04; mem_used: 74.8 MB; procmem: 432.7 MiB
[>-----------] 6/100 eta: 1m; 00:00:05; mem_used: 74.8 MB; procmem: 458.3 MiB
[>-----------] 7/100 eta: 1m; 00:00:06; mem_used: 74.8 MB; procmem: 463.3 MiB
[>-----------] 8/100 eta: 1m; 00:00:07; mem_used: 74.8 MB; procmem: 472.1 MiB
[>-----------] 9/100 eta: 1m; 00:00:08; mem_used: 74.8 MB; procmem: 472.2 MiB
[>------------] 10/100 eta: 1m; 00:00:09; mem_used: 74.8 MB; procmem: 482 MiB
[>----------] 11/100 eta: 1m; 00:00:10; mem_used: 74.8 MB; procmem: 484.2 MiB
[=>-----------] 12/100 eta: 1m; 00:00:12; mem_used: 74.8 MB; procmem: 494 MiB
[=>-----------] 13/100 eta: 1m; 00:00:13; mem_used: 74.8 MB; procmem: 494 MiB
[=>---------] 14/100 eta: 1m; 00:00:14; mem_used: 74.8 MB; procmem: 501.1 MiB
[=>---------] 15/100 eta: 1m; 00:00:15; mem_used: 74.8 MB; procmem: 519.1 MiB
[=>---------] 16/100 eta: 1m; 00:00:16; mem_used: 74.8 MB; procmem: 519.9 MiB
[=>---------] 17/100 eta: 1m; 00:00:17; mem_used: 74.8 MB; procmem: 529.7 MiB
[=>---------] 18/100 eta: 1m; 00:00:17; mem_used: 74.8 MB; procmem: 534.6 MiB
[=>-----------] 19/100 eta: 1m; 00:00:19; mem_used: 74.8 MB; procmem: 542 MiB
[==>----------] 20/100 eta: 1m; 00:00:20; mem_used: 74.8 MB; procmem: 542 MiB
[==>----------] 21/100 eta: 1m; 00:00:21; mem_used: 74.8 MB; procmem: 542 MiB
[=>---------] 22/100 eta: 1m; 00:00:22; mem_used: 74.8 MB; procmem: 542.1 MiB
[==>--------] 23/100 eta: 1m; 00:00:23; mem_used: 74.8 MB; procmem: 544.5 MiB
[==>----------] 24/100 eta: 1m; 00:00:24; mem_used: 74.8 MB; procmem: 547 MiB
[==>--------] 25/100 eta: 1m; 00:00:25; mem_used: 74.8 MB; procmem: 547.6 MiB
[==>--------] 26/100 eta: 1m; 00:00:26; mem_used: 74.8 MB; procmem: 547.6 MiB
[===>---------] 27/100 eta: 1m; 00:00:27; mem_used: 74.8 MB; procmem: 550 MiB
[==>--------] 28/100 eta: 1m; 00:00:28; mem_used: 74.8 MB; procmem: 554.9 MiB
[===>---------] 29/100 eta: 1m; 00:00:29; mem_used: 74.8 MB; procmem: 555 MiB
[===>---------] 30/100 eta: 1m; 00:00:30; mem_used: 74.8 MB; procmem: 555 MiB
[==>--------] 31/100 eta: 1m; 00:00:31; mem_used: 74.8 MB; procmem: 555.1 MiB
[===>-------] 32/100 eta: 1m; 00:00:32; mem_used: 74.8 MB; procmem: 555.1 MiB
[===>-------] 33/100 eta: 1m; 00:00:33; mem_used: 74.8 MB; procmem: 555.1 MiB
[===>-------] 34/100 eta: 1m; 00:00:34; mem_used: 74.8 MB; procmem: 555.1 MiB
[===>-------] 35/100 eta: 1m; 00:00:35; mem_used: 74.8 MB; procmem: 555.1 MiB
[===>-------] 36/100 eta: 1m; 00:00:36; mem_used: 74.8 MB; procmem: 547.1 MiB
[===>-------] 37/100 eta: 1m; 00:00:37; mem_used: 74.8 MB; procmem: 547.1 MiB
[====>--------] 38/100 eta: 1m; 00:00:38; mem_used: 74.8 MB; procmem: 552 MiB
[====>--------] 39/100 eta: 1m; 00:00:39; mem_used: 74.8 MB; procmem: 552 MiB
[===>-------] 40/100 eta: 1m; 00:00:41; mem_used: 74.8 MB; procmem: 552.2 MiB
[====>--------] 41/100 eta: 1m; 00:00:42; mem_used: 74.8 MB; procmem: 557 MiB
[====>--------] 42/100 eta: 1m; 00:00:43; mem_used: 74.8 MB; procmem: 557 MiB
[=====>-------] 43/100 eta: 1m; 00:00:44; mem_used: 74.8 MB; procmem: 557 MiB
[=====>-------] 44/100 eta: 1m; 00:00:45; mem_used: 74.8 MB; procmem: 557 MiB
[====>------] 45/100 eta: 1m; 00:00:46; mem_used: 74.8 MB; procmem: 559.5 MiB
[====>------] 46/100 eta: 1m; 00:00:47; mem_used: 74.8 MB; procmem: 564.4 MiB
[====>------] 47/100 eta: 1m; 00:00:48; mem_used: 74.8 MB; procmem: 564.4 MiB
[====>------] 48/100 eta: 1m; 00:00:49; mem_used: 74.8 MB; procmem: 564.4 MiB
[====>------] 49/100 eta: 1m; 00:00:50; mem_used: 74.8 MB; procmem: 564.4 MiB
[=====>-----] 50/100 eta: 1m; 00:00:51; mem_used: 74.8 MB; procmem: 565.4 MiB
[=====>-----] 51/100 eta: 1m; 00:00:52; mem_used: 74.8 MB; procmem: 566.5 MiB
[=====>-----] 52/100 eta:50s; 00:00:53; mem_used: 74.8 MB; procmem: 566.5 MiB
[=====>-----] 53/100 eta:49s; 00:00:54; mem_used: 74.8 MB; procmem: 564.4 MiB
[=====>-----] 54/100 eta:48s; 00:00:55; mem_used: 74.8 MB; procmem: 565.4 MiB
[=====>-----] 55/100 eta:47s; 00:00:56; mem_used: 74.8 MB; procmem: 569.8 MiB
[=====>-----] 56/100 eta:46s; 00:00:57; mem_used: 74.8 MB; procmem: 572.5 MiB
[=====>-----] 57/100 eta:44s; 00:00:58; mem_used: 74.8 MB; procmem: 580.4 MiB
[=====>-----] 58/100 eta:43s; 00:01:00; mem_used: 74.8 MB; procmem: 580.4 MiB
[=====>-----] 59/100 eta:42s; 00:01:01; mem_used: 74.8 MB; procmem: 580.4 MiB
[======>----] 60/100 eta:41s; 00:01:02; mem_used: 74.8 MB; procmem: 580.4 MiB
[======>----] 61/100 eta:40s; 00:01:03; mem_used: 74.8 MB; procmem: 580.4 MiB
[======>----] 62/100 eta:39s; 00:01:04; mem_used: 74.8 MB; procmem: 585.3 MiB
[======>----] 63/100 eta:38s; 00:01:05; mem_used: 74.8 MB; procmem: 585.4 MiB
[======>----] 64/100 eta:37s; 00:01:06; mem_used: 74.8 MB; procmem: 585.4 MiB
[======>----] 65/100 eta:36s; 00:01:07; mem_used: 74.8 MB; procmem: 585.4 MiB
[======>----] 66/100 eta:35s; 00:01:08; mem_used: 74.8 MB; procmem: 585.4 MiB
[======>----] 67/100 eta:34s; 00:01:09; mem_used: 74.8 MB; procmem: 587.9 MiB
[======>----] 68/100 eta:33s; 00:01:10; mem_used: 74.8 MB; procmem: 579.9 MiB
[=======>---] 69/100 eta:32s; 00:01:11; mem_used: 74.8 MB; procmem: 581.2 MiB
[=======>---] 70/100 eta:31s; 00:01:12; mem_used: 74.8 MB; procmem: 581.2 MiB
[=======>---] 71/100 eta:30s; 00:01:13; mem_used: 74.8 MB; procmem: 581.2 MiB
[=======>---] 72/100 eta:29s; 00:01:14; mem_used: 74.8 MB; procmem: 581.2 MiB
[=======>---] 73/100 eta:28s; 00:01:15; mem_used: 74.8 MB; procmem: 581.2 MiB
[=======>---] 74/100 eta:27s; 00:01:16; mem_used: 74.8 MB; procmem: 581.2 MiB
[=======>---] 75/100 eta:26s; 00:01:17; mem_used: 74.8 MB; procmem: 573.2 MiB
[=======>---] 76/100 eta:25s; 00:01:18; mem_used: 74.8 MB; procmem: 573.3 MiB
[=======>---] 77/100 eta:24s; 00:01:20; mem_used: 74.8 MB; procmem: 573.3 MiB
[========>--] 78/100 eta:23s; 00:01:21; mem_used: 74.8 MB; procmem: 573.3 MiB
[========>--] 79/100 eta:22s; 00:01:22; mem_used: 74.8 MB; procmem: 573.3 MiB
[========>--] 80/100 eta:21s; 00:01:23; mem_used: 74.8 MB; procmem: 573.3 MiB
[========>--] 81/100 eta:20s; 00:01:24; mem_used: 74.8 MB; procmem: 575.8 MiB
[========>--] 82/100 eta:19s; 00:01:25; mem_used: 74.8 MB; procmem: 575.8 MiB
[========>--] 83/100 eta:18s; 00:01:26; mem_used: 74.8 MB; procmem: 575.8 MiB
[========>--] 84/100 eta:17s; 00:01:27; mem_used: 74.8 MB; procmem: 575.8 MiB
[========>--] 85/100 eta:16s; 00:01:28; mem_used: 74.8 MB; procmem: 575.8 MiB
[==========>--] 86/100 eta:15s; 00:01:29; mem_used: 74.8 MB; procmem: 576 MiB
[==========>--] 87/100 eta:14s; 00:01:30; mem_used: 74.8 MB; procmem: 576 MiB
[=========>-] 88/100 eta:13s; 00:01:31; mem_used: 74.8 MB; procmem: 579.5 MiB
[===========>-] 89/100 eta:11s; 00:01:32; mem_used: 74.8 MB; procmem: 576 MiB
[===========>-] 90/100 eta:10s; 00:01:33; mem_used: 74.8 MB; procmem: 576 MiB
[=========>-] 91/100 eta: 9s; 00:01:34; mem_used: 74.8 MB; procmem: 581.8 MiB
[=========>-] 92/100 eta: 8s; 00:01:35; mem_used: 74.8 MB; procmem: 589.2 MiB
[=========>-] 93/100 eta: 7s; 00:01:36; mem_used: 74.8 MB; procmem: 589.2 MiB
[=========>-] 94/100 eta: 6s; 00:01:37; mem_used: 74.8 MB; procmem: 589.2 MiB
[=========>-] 95/100 eta: 5s; 00:01:39; mem_used: 74.8 MB; procmem: 589.2 MiB
[==========>] 96/100 eta: 4s; 00:01:40; mem_used: 74.8 MB; procmem: 589.2 MiB
[==========>] 97/100 eta: 3s; 00:01:41; mem_used: 74.8 MB; procmem: 589.2 MiB
[==========>] 98/100 eta: 2s; 00:01:42; mem_used: 74.8 MB; procmem: 589.2 MiB
[==========>] 99/100 eta: 1s; 00:01:43; mem_used: 74.8 MB; procmem: 590.4 MiB
[==========] 100/100 eta: 0s; 00:01:44; mem_used: 74.8 MB; procmem: 598.5 MiB

Stopping plumber API
...sleeping...
Starting plumber API
...sleeping...
Profiling server: return
[------------] 1/100 eta: 0s; 00:00:00; mem_used: 71.9 MB; procmem: 284.8 MiB
[------------] 2/100 eta: 1m; 00:00:01; mem_used: 74.8 MB; procmem: 315.4 MiB
[------------] 3/100 eta: 1m; 00:00:02; mem_used: 74.8 MB; procmem: 333.6 MiB
[------------] 4/100 eta: 1m; 00:00:03; mem_used: 74.8 MB; procmem: 336.6 MiB
[>-----------] 5/100 eta: 1m; 00:00:04; mem_used: 74.8 MB; procmem: 339.9 MiB
[>-----------] 6/100 eta: 1m; 00:00:05; mem_used: 74.8 MB; procmem: 343.2 MiB
[>-----------] 7/100 eta: 1m; 00:00:06; mem_used: 74.8 MB; procmem: 346.6 MiB
[>-----------] 8/100 eta: 1m; 00:00:07; mem_used: 74.8 MB; procmem: 350.2 MiB
[>-----------] 9/100 eta: 1m; 00:00:07; mem_used: 74.8 MB; procmem: 351.9 MiB
[>------------] 10/100 eta: 1m; 00:00:08; mem_used: 74.8 MB; procmem: 352 MiB
[>----------] 11/100 eta: 1m; 00:00:09; mem_used: 74.8 MB; procmem: 354.4 MiB
[>----------] 12/100 eta: 1m; 00:00:10; mem_used: 74.8 MB; procmem: 354.6 MiB
[>----------] 13/100 eta: 1m; 00:00:11; mem_used: 74.8 MB; procmem: 354.6 MiB
[=>---------] 14/100 eta: 1m; 00:00:12; mem_used: 74.8 MB; procmem: 357.3 MiB
[=>---------] 15/100 eta: 1m; 00:00:13; mem_used: 74.8 MB; procmem: 359.7 MiB
[=>---------] 16/100 eta: 1m; 00:00:14; mem_used: 74.8 MB; procmem: 359.8 MiB
[=>---------] 17/100 eta: 1m; 00:00:15; mem_used: 74.8 MB; procmem: 359.8 MiB
[=>---------] 18/100 eta: 1m; 00:00:16; mem_used: 74.8 MB; procmem: 359.8 MiB
[=>---------] 19/100 eta: 1m; 00:00:17; mem_used: 74.8 MB; procmem: 362.3 MiB
[=>---------] 20/100 eta: 1m; 00:00:18; mem_used: 74.8 MB; procmem: 367.1 MiB
[=>---------] 21/100 eta: 1m; 00:00:19; mem_used: 74.8 MB; procmem: 367.9 MiB
[=>---------] 22/100 eta: 1m; 00:00:20; mem_used: 74.8 MB; procmem: 367.9 MiB
[==>--------] 23/100 eta: 1m; 00:00:21; mem_used: 74.8 MB; procmem: 367.9 MiB
[==>--------] 24/100 eta: 1m; 00:00:22; mem_used: 74.8 MB; procmem: 367.9 MiB
[==>--------] 25/100 eta: 1m; 00:00:23; mem_used: 74.8 MB; procmem: 367.9 MiB
[==>--------] 26/100 eta: 1m; 00:00:24; mem_used: 74.8 MB; procmem: 368.6 MiB
[==>--------] 27/100 eta: 1m; 00:00:25; mem_used: 74.8 MB; procmem: 368.6 MiB
[==>--------] 28/100 eta: 1m; 00:00:26; mem_used: 74.8 MB; procmem: 368.6 MiB
[==>--------] 29/100 eta: 1m; 00:00:27; mem_used: 74.8 MB; procmem: 368.8 MiB
[==>--------] 30/100 eta: 1m; 00:00:28; mem_used: 74.8 MB; procmem: 369.4 MiB
[==>--------] 31/100 eta: 1m; 00:00:29; mem_used: 74.8 MB; procmem: 371.8 MiB
[===>-------] 32/100 eta: 1m; 00:00:30; mem_used: 74.8 MB; procmem: 371.8 MiB
[===>-------] 33/100 eta: 1m; 00:00:31; mem_used: 74.8 MB; procmem: 371.8 MiB
[===>-------] 34/100 eta: 1m; 00:00:32; mem_used: 74.8 MB; procmem: 371.8 MiB
[===>-------] 35/100 eta: 1m; 00:00:33; mem_used: 74.8 MB; procmem: 371.8 MiB
[===>-------] 36/100 eta: 1m; 00:00:34; mem_used: 74.8 MB; procmem: 372.5 MiB
[===>-------] 37/100 eta: 1m; 00:00:35; mem_used: 74.8 MB; procmem: 372.5 MiB
[===>-------] 38/100 eta: 1m; 00:00:35; mem_used: 74.8 MB; procmem: 372.5 MiB
[===>-------] 39/100 eta: 1m; 00:00:37; mem_used: 74.8 MB; procmem: 372.5 MiB
[====>--------] 40/100 eta: 1m; 00:00:38; mem_used: 74.8 MB; procmem: 375 MiB
[====>------] 41/100 eta: 1m; 00:00:38; mem_used: 74.8 MB; procmem: 377.4 MiB
[====>------] 42/100 eta: 1m; 00:00:39; mem_used: 74.8 MB; procmem: 378.2 MiB
[====>------] 43/100 eta: 1m; 00:00:40; mem_used: 74.8 MB; procmem: 378.5 MiB
[====>------] 44/100 eta: 1m; 00:00:41; mem_used: 74.8 MB; procmem: 383.4 MiB
[====>------] 45/100 eta: 1m; 00:00:42; mem_used: 74.8 MB; procmem: 383.4 MiB
[====>------] 46/100 eta: 1m; 00:00:43; mem_used: 74.8 MB; procmem: 385.8 MiB
[====>------] 47/100 eta: 1m; 00:00:44; mem_used: 74.8 MB; procmem: 386.2 MiB
[====>------] 48/100 eta:50s; 00:00:45; mem_used: 74.8 MB; procmem: 386.2 MiB
[====>------] 49/100 eta:49s; 00:00:46; mem_used: 74.8 MB; procmem: 386.2 MiB
[=====>-----] 50/100 eta:48s; 00:00:47; mem_used: 74.8 MB; procmem: 386.2 MiB
[=====>-----] 51/100 eta:47s; 00:00:48; mem_used: 74.8 MB; procmem: 386.2 MiB
[=====>-----] 52/100 eta:46s; 00:00:49; mem_used: 74.8 MB; procmem: 386.2 MiB
[=====>-----] 53/100 eta:45s; 00:00:50; mem_used: 74.8 MB; procmem: 386.2 MiB
[=====>-----] 54/100 eta:44s; 00:00:51; mem_used: 74.8 MB; procmem: 386.2 MiB
[=====>-----] 55/100 eta:43s; 00:00:52; mem_used: 74.8 MB; procmem: 386.2 MiB
[=====>-----] 56/100 eta:42s; 00:00:53; mem_used: 74.8 MB; procmem: 386.2 MiB
[=====>-----] 57/100 eta:41s; 00:00:54; mem_used: 74.8 MB; procmem: 386.2 MiB
[=====>-----] 58/100 eta:40s; 00:00:55; mem_used: 74.8 MB; procmem: 386.2 MiB
[=====>-----] 59/100 eta:39s; 00:00:56; mem_used: 74.8 MB; procmem: 391.1 MiB
[======>----] 60/100 eta:38s; 00:00:57; mem_used: 74.8 MB; procmem: 391.1 MiB
[======>----] 61/100 eta:38s; 00:00:58; mem_used: 74.8 MB; procmem: 391.2 MiB
[======>----] 62/100 eta:37s; 00:00:59; mem_used: 74.8 MB; procmem: 391.2 MiB
[======>----] 63/100 eta:36s; 00:01:00; mem_used: 74.8 MB; procmem: 391.2 MiB
[======>----] 64/100 eta:35s; 00:01:01; mem_used: 74.8 MB; procmem: 391.2 MiB
[======>----] 65/100 eta:34s; 00:01:02; mem_used: 74.8 MB; procmem: 391.2 MiB
[======>----] 66/100 eta:33s; 00:01:03; mem_used: 74.8 MB; procmem: 391.4 MiB
[======>----] 67/100 eta:32s; 00:01:04; mem_used: 74.8 MB; procmem: 391.4 MiB
[======>----] 68/100 eta:31s; 00:01:05; mem_used: 74.8 MB; procmem: 391.4 MiB
[=======>---] 69/100 eta:30s; 00:01:06; mem_used: 74.8 MB; procmem: 391.4 MiB
[=======>---] 70/100 eta:29s; 00:01:07; mem_used: 74.8 MB; procmem: 391.4 MiB
[=======>---] 71/100 eta:28s; 00:01:08; mem_used: 74.8 MB; procmem: 391.4 MiB
[=======>---] 72/100 eta:27s; 00:01:09; mem_used: 74.8 MB; procmem: 391.4 MiB
[=======>---] 73/100 eta:26s; 00:01:10; mem_used: 74.8 MB; procmem: 391.4 MiB
[=======>---] 74/100 eta:25s; 00:01:11; mem_used: 74.8 MB; procmem: 391.4 MiB
[=======>---] 75/100 eta:24s; 00:01:12; mem_used: 74.8 MB; procmem: 391.4 MiB
[=======>---] 76/100 eta:23s; 00:01:13; mem_used: 74.8 MB; procmem: 391.4 MiB
[=======>---] 77/100 eta:22s; 00:01:14; mem_used: 74.8 MB; procmem: 391.4 MiB
[========>--] 78/100 eta:21s; 00:01:15; mem_used: 74.8 MB; procmem: 391.4 MiB
[========>--] 79/100 eta:20s; 00:01:16; mem_used: 74.8 MB; procmem: 391.4 MiB
[========>--] 80/100 eta:19s; 00:01:17; mem_used: 74.8 MB; procmem: 391.4 MiB
[========>--] 81/100 eta:18s; 00:01:18; mem_used: 74.8 MB; procmem: 391.4 MiB
[========>--] 82/100 eta:17s; 00:01:19; mem_used: 74.8 MB; procmem: 391.4 MiB
[========>--] 83/100 eta:16s; 00:01:20; mem_used: 74.8 MB; procmem: 391.4 MiB
[========>--] 84/100 eta:16s; 00:01:21; mem_used: 74.8 MB; procmem: 391.4 MiB
[========>--] 85/100 eta:15s; 00:01:22; mem_used: 74.8 MB; procmem: 391.4 MiB
[========>--] 86/100 eta:14s; 00:01:23; mem_used: 74.8 MB; procmem: 391.4 MiB
[=========>-] 87/100 eta:13s; 00:01:24; mem_used: 74.8 MB; procmem: 391.4 MiB
[=========>-] 88/100 eta:12s; 00:01:25; mem_used: 74.8 MB; procmem: 391.4 MiB
[=========>-] 89/100 eta:11s; 00:01:26; mem_used: 74.8 MB; procmem: 391.4 MiB
[=========>-] 90/100 eta:10s; 00:01:27; mem_used: 74.8 MB; procmem: 391.4 MiB
[===========>-] 91/100 eta: 9s; 00:01:28; mem_used: 74.8 MB; procmem: 392 MiB
[===========>-] 92/100 eta: 8s; 00:01:29; mem_used: 74.8 MB; procmem: 392 MiB
[===========>-] 93/100 eta: 7s; 00:01:30; mem_used: 74.8 MB; procmem: 392 MiB
[===========>-] 94/100 eta: 6s; 00:01:31; mem_used: 74.8 MB; procmem: 392 MiB
[===========>-] 95/100 eta: 5s; 00:01:32; mem_used: 74.8 MB; procmem: 392 MiB
[===========>-] 96/100 eta: 4s; 00:01:33; mem_used: 74.8 MB; procmem: 392 MiB
[============>] 97/100 eta: 3s; 00:01:34; mem_used: 74.8 MB; procmem: 392 MiB
[============>] 98/100 eta: 2s; 00:01:35; mem_used: 74.8 MB; procmem: 392 MiB
[==========>] 99/100 eta: 1s; 00:01:36; mem_used: 74.8 MB; procmem: 392.2 MiB
[==========] 100/100 eta: 0s; 00:01:37; mem_used: 74.8 MB; procmem: 392.2 MiB

Stopping plumber API
...sleeping...
Generate Image
Parsed with column specification:
cols(
  route = col_character(),
  row = col_double(),
  mem_used = col_character(),
  procmem = col_character(),
  time = col_double()
)
mem_test: 

schloerke avatar Oct 09 '19 20:10 schloerke

A little more context: the pryr::mem_used() function reports the amount of memory consumed by R objects. The memuse::Sys.procmem() function reports how much memory is used by the process, according to the operating system.

We found that the amount of memory reported by pryr::mem_used() does not increase, but the memory reported by memuse::Sys.procmem()does increase. This means that the increase is due to C-level memory use. The question then is, is the increase in memory usage due to some sort of cache/buffers, or is it a memory leak?

We don't think it's a memory leak for a couple of reasons. First, the memory use appears to be asymptotic. Secondly, the memory use increase even if plumber isn't involved. If you repeatedly call do.call(rbind, replicate(10000, mtcars, simplify = F)) from the R prompt, the memory usage also increases asymptotically.

So it seems that the increase in memory usage is probably due to some sort of cache or buffers. I don't know enough about R's internals to say exactly what it is, though.

wch avatar Oct 09 '19 21:10 wch

I was able to reproduce this error using a basic R function to prove that it's not a problem for plumber:

test <- function(n = 10000) {
  dump = do.call(dplyr::rbind, replicate(n, mtcars, simplify = F))
  return()
}

Sys.sleep(5)
test()
Sys.sleep(5)
gc()

In the beginning, the R process allocated 35 MB RAM. After the first call of test(), the memory usage increased to 158 MB and dropped to 111 MB after calling gc(). So there is still a difference of 76 MB not beeing released.

Normally this wouldn't be much of a problem for a R script running once in a while. But as plumber ususally ist up 24/7 this leads to a huge memory leak. Should we report this to the developers of R?

In my use case, I am deploying plumber in a docker container (replication factor: 15) using HAProxy as load balancer. If large datasets are requested one after another, this causes the memory usage of each container to grow to significant sizes making it impossible to host on ordinary machines. The only solution I found was restarting the container. Do you see any solution for a programmatic restart of plumber as a temporary fix? I was thinking about some postrequesthook or any other possibility.

Thank you for your help.

LukasK13 avatar Oct 10 '19 06:10 LukasK13

Adding reprex output...

test_mem <- function(n = 100) {
  # generate data
  ignore <- do.call(rbind, replicate(n, ggplot2::diamonds, simplify = F))
  return()
}
print_mem <- function() {
  withAutoprint({
    gc()
    pryr::mem_used()
    memuse::Sys.procmem()
  })
}

print_mem()
#> > gc()
#>          used (Mb) gc trigger (Mb) limit (Mb) max used (Mb)
#> Ncells 476833 25.5    1030405 55.1         NA   663948 35.5
#> Vcells 887935  6.8    8388608 64.0      32768  1822016 14.0
#> > pryr::mem_used()
#> Registered S3 method overwritten by 'pryr':
#>   method      from
#>   print.bytes Rcpp
#> 34.2 MB
#> > memuse::Sys.procmem()
#> Size:  85.660 MiB

test_mem()
#> NULL
print_mem()
#> > gc()
#>           used (Mb) gc trigger  (Mb) limit (Mb) max used  (Mb)
#> Ncells  689509 36.9    1288835  68.9         NA  1030405  55.1
#> Vcells 1667893 12.8   76694957 585.2      32768 94968494 724.6
#> > pryr::mem_used()
#> 52 MB
#> > memuse::Sys.procmem()
#> Size:  2.477 GiB

test_mem()
#> NULL
print_mem()
#> > gc()
#>           used (Mb) gc trigger  (Mb) limit (Mb) max used  (Mb)
#> Ncells  690815 36.9    1288835  68.9         NA  1030405  55.1
#> Vcells 1670977 12.8   68087749 519.5      32768 94968494 724.6
#> > pryr::mem_used()
#> 52.1 MB
#> > memuse::Sys.procmem()
#> Size:  2.164 GiB

Created on 2019-10-10 by the reprex package (v0.3.0)

schloerke avatar Oct 10 '19 13:10 schloerke

@schloerke I can't reproduce your code on Mac. Do you use Mac as well? I've run the code several times. The first time, the total memory used increases by about 50 MB. The other runs show no increases in memory use at all (yeah, maybe for one time increase 150MB but reduced in the later runs).

Is it possible even if R has told the system "I have released some memory. When you need it please come and take it back" but the system currently has plenty of memories so it doesn't ask R to return that part of memory back?

shrektan avatar Oct 10 '19 15:10 shrektan

@shrektan I am on Mac as well. I'm able to reproduce your situation as well. I've found that after a certain threshold, the memory is not reclaimed by the OS.

Is it possible the OS wont reclaim memory that has been released?

Yes. I believe this is what we are seeing.

Also... Linux does not reclaim memory from a process. Docker is running linux. Therefore once the memory is obtained by the R process, it'll never leave.

I do not know why the already obtained memory is not being repurposed, causing the R process to balloon in memory usage.😞

Funny website about linux memory

Reclaim
print_mem <- function() {
  withAutoprint({
    gc()
    pryr::mem_used()
    memuse::Sys.procmem()
  })
}
print_mem()
#> > gc()
#>          used (Mb) gc trigger (Mb) limit (Mb) max used (Mb)
#> Ncells 476978 25.5    1030820 55.1         NA   663948 35.5
#> Vcells 888952  6.8    8388608 64.0      32768  1821939 14.0
#> > pryr::mem_used()
#> Registered S3 method overwritten by 'pryr':
#>   method      from
#>   print.bytes Rcpp
#> 34.2 MB
#> > memuse::Sys.procmem()
#> Size:  89.531 MiB
x <- rnorm(1e8)
print_mem()
#> > gc()
#>             used  (Mb) gc trigger   (Mb) limit (Mb)  max used  (Mb)
#> Ncells    579261  31.0    1030820   55.1         NA    725551  38.8
#> Vcells 101089962 771.3  147978526 1129.0      32768 101529869 774.7
#> > pryr::mem_used()
#> 841 MB
#> > memuse::Sys.procmem()
#> Size:  856.555 MiB
# ~850mb. Great!
rm(x)
print_mem()
#> > gc()
#>           used (Mb) gc trigger  (Mb) limit (Mb)  max used  (Mb)
#> Ncells  579291 31.0    1030820  55.1         NA    725551  38.8
#> Vcells 1090032  8.4  118382821 903.2      32768 101529869 774.7
#> > pryr::mem_used()
#> 41.2 MB
#> > memuse::Sys.procmem()
#> Size:  93.613 MiB
# ~90mb. Great!
x <- rnorm(1e8)
# ~ 1 GB used
print_mem()
#> > gc()
#>             used  (Mb) gc trigger   (Mb) limit (Mb)  max used  (Mb)
#> Ncells    579313  31.0    1030820   55.1         NA    725551  38.8
#> Vcells 101090068 771.3  148161554 1130.4      32768 101529869 774.7
#> > pryr::mem_used()
#> 841 MB
#> > memuse::Sys.procmem()
#> Size:  856.574 MiB
# ~850mb. Great!
rm(x)
print_mem()
#> > gc()
#>           used (Mb) gc trigger  (Mb) limit (Mb)  max used  (Mb)
#> Ncells  579343 31.0    1030820  55.1         NA    725551  38.8
#> Vcells 1090138  8.4  118529244 904.4      32768 101529869 774.7
#> > pryr::mem_used()
#> 41.2 MB
#> > memuse::Sys.procmem()
#> Size:  93.648 MiB
# ~90mb. Great!
ignore <- do.call(rbind, replicate(100, ggplot2::diamonds, simplify = F))
print_mem()
#> > gc()
#>            used  (Mb) gc trigger  (Mb) limit (Mb)  max used  (Mb)
#> Ncells   689584  36.9    1291022  69.0         NA   1030820  55.1
#> Vcells 45506049 347.2   91110460 695.2      32768 101529869 774.7
#> > pryr::mem_used()
#> 403 MB
#> > memuse::Sys.procmem()
#> Size:  2.972 GiB
# ~3GB. Great!
rm(ignore)
print_mem()
#> > gc()
#>           used (Mb) gc trigger  (Mb) limit (Mb)  max used  (Mb)
#> Ncells  689588 36.9    1291022  69.0         NA   1030820  55.1
#> Vcells 1669033 12.8   72888368 556.1      32768 101529869 774.7
#> > pryr::mem_used()
#> 52 MB
#> > memuse::Sys.procmem()
#> Size:  2.724 GiB
# Not ~90mb. :-(
ignore <- do.call(rbind, replicate(100, ggplot2::diamonds, simplify = F))
print_mem()
#> > gc()
#>            used  (Mb) gc trigger  (Mb) limit (Mb)  max used  (Mb)
#> Ncells   689630  36.9    1291022  69.0         NA   1030820  55.1
#> Vcells 45506150 347.2   97159884 741.3      32768 101529869 774.7
#> > pryr::mem_used()
#> 403 MB
#> > memuse::Sys.procmem()
#> Size:  2.753 GiB
rm(ignore)
print_mem()
#> > gc()
#>           used (Mb) gc trigger  (Mb) limit (Mb)  max used  (Mb)
#> Ncells  689628 36.9    1291022  69.0         NA   1030820  55.1
#> Vcells 1669130 12.8   77727908 593.1      32768 101529869 774.7
#> > pryr::mem_used()
#> 52 MB
#> > memuse::Sys.procmem()
#> Size:  2.670 GiB
# Not ~90mb. :-(
for (i in 1:20) {
  print_mem()
}
#> > gc()
#>           used (Mb) gc trigger  (Mb) limit (Mb)  max used  (Mb)
#> Ncells  691274 37.0    1291022  69.0         NA   1030820  55.1
#> Vcells 1672723 12.8   39796690 303.7      32768 101529869 774.7
#> > pryr::mem_used()
#> 52.1 MB
#> > memuse::Sys.procmem()
#> Size:  2.670 GiB 
#> > gc()
#>           used (Mb) gc trigger  (Mb) limit (Mb)  max used  (Mb)
#> Ncells  691278 37.0    1291022  69.0         NA   1030820  55.1
#> Vcells 1672770 12.8   20375906 155.5      32768 101529869 774.7
#> > pryr::mem_used()
#> 52.1 MB
#> > memuse::Sys.procmem()
#> Size:  2.670 GiB 
#> > gc()
#>           used (Mb) gc trigger (Mb) limit (Mb)  max used  (Mb)
#> Ncells  691280 37.0    1291022 69.0         NA   1030820  55.1
#> Vcells 1672787 12.8   10432464 79.6      32768 101529869 774.7
#> > pryr::mem_used()
#> 52.1 MB
#> > memuse::Sys.procmem()
#> Size:  2.670 GiB 
#> > gc()
#>           used (Mb) gc trigger (Mb) limit (Mb)  max used  (Mb)
#> Ncells  691283 37.0    1291022 69.0         NA   1030820  55.1
#> Vcells 1672819 12.8   10432464 79.6      32768 101529869 774.7
#> > pryr::mem_used()
#> 52.1 MB
#> > memuse::Sys.procmem()
#> Size:  2.670 GiB 
#> > gc()
#>           used (Mb) gc trigger (Mb) limit (Mb)  max used  (Mb)
#> Ncells  691285 37.0    1291022 69.0         NA   1030820  55.1
#> Vcells 1672843 12.8   10432464 79.6      32768 101529869 774.7
#> > pryr::mem_used()
#> 52.1 MB
#> > memuse::Sys.procmem()
#> Size:  2.670 GiB 
#> > gc()
#>           used (Mb) gc trigger (Mb) limit (Mb)  max used  (Mb)
#> Ncells  691287 37.0    1291022 69.0         NA   1030820  55.1
#> Vcells 1672867 12.8   10432464 79.6      32768 101529869 774.7
#> > pryr::mem_used()
#> 52.1 MB
#> > memuse::Sys.procmem()
#> Size:  2.670 GiB 
#> > gc()
#>           used (Mb) gc trigger (Mb) limit (Mb)  max used  (Mb)
#> Ncells  691289 37.0    1291022 69.0         NA   1030820  55.1
#> Vcells 1672891 12.8   10432464 79.6      32768 101529869 774.7
#> > pryr::mem_used()
#> 52.1 MB
#> > memuse::Sys.procmem()
#> Size:  2.670 GiB 
#> > gc()
#>           used (Mb) gc trigger (Mb) limit (Mb)  max used  (Mb)
#> Ncells  691291 37.0    1291022 69.0         NA   1030820  55.1
#> Vcells 1672915 12.8   10432464 79.6      32768 101529869 774.7
#> > pryr::mem_used()
#> 52.1 MB
#> > memuse::Sys.procmem()
#> Size:  2.670 GiB 
#> > gc()
#>           used (Mb) gc trigger (Mb) limit (Mb)  max used  (Mb)
#> Ncells  691293 37.0    1291022 69.0         NA   1030820  55.1
#> Vcells 1672939 12.8   10432464 79.6      32768 101529869 774.7
#> > pryr::mem_used()
#> 52.1 MB
#> > memuse::Sys.procmem()
#> Size:  2.670 GiB 
#> > gc()
#>           used (Mb) gc trigger (Mb) limit (Mb)  max used  (Mb)
#> Ncells  691295 37.0    1291022 69.0         NA   1030820  55.1
#> Vcells 1672963 12.8   10432464 79.6      32768 101529869 774.7
#> > pryr::mem_used()
#> 52.1 MB
#> > memuse::Sys.procmem()
#> Size:  2.670 GiB 
#> > gc()
#>           used (Mb) gc trigger (Mb) limit (Mb)  max used  (Mb)
#> Ncells  691297 37.0    1291022 69.0         NA   1030820  55.1
#> Vcells 1672987 12.8   10432464 79.6      32768 101529869 774.7
#> > pryr::mem_used()
#> 52.1 MB
#> > memuse::Sys.procmem()
#> Size:  2.670 GiB 
#> > gc()
#>           used (Mb) gc trigger (Mb) limit (Mb)  max used  (Mb)
#> Ncells  691299 37.0    1291022 69.0         NA   1030820  55.1
#> Vcells 1673011 12.8   10432464 79.6      32768 101529869 774.7
#> > pryr::mem_used()
#> 52.1 MB
#> > memuse::Sys.procmem()
#> Size:  2.670 GiB 
#> > gc()
#>           used (Mb) gc trigger (Mb) limit (Mb)  max used  (Mb)
#> Ncells  691301 37.0    1291022 69.0         NA   1030820  55.1
#> Vcells 1673035 12.8   10432464 79.6      32768 101529869 774.7
#> > pryr::mem_used()
#> 52.1 MB
#> > memuse::Sys.procmem()
#> Size:  2.670 GiB 
#> > gc()
#>           used (Mb) gc trigger (Mb) limit (Mb)  max used  (Mb)
#> Ncells  691303 37.0    1291022 69.0         NA   1030820  55.1
#> Vcells 1673059 12.8   10432464 79.6      32768 101529869 774.7
#> > pryr::mem_used()
#> 52.1 MB
#> > memuse::Sys.procmem()
#> Size:  2.670 GiB 
#> > gc()
#>           used (Mb) gc trigger (Mb) limit (Mb)  max used  (Mb)
#> Ncells  691305 37.0    1291022 69.0         NA   1030820  55.1
#> Vcells 1673083 12.8   10432464 79.6      32768 101529869 774.7
#> > pryr::mem_used()
#> 52.1 MB
#> > memuse::Sys.procmem()
#> Size:  2.670 GiB 
#> > gc()
#>           used (Mb) gc trigger (Mb) limit (Mb)  max used  (Mb)
#> Ncells  691307 37.0    1291022 69.0         NA   1030820  55.1
#> Vcells 1673107 12.8   10432464 79.6      32768 101529869 774.7
#> > pryr::mem_used()
#> 52.1 MB
#> > memuse::Sys.procmem()
#> Size:  2.670 GiB 
#> > gc()
#>           used (Mb) gc trigger (Mb) limit (Mb)  max used  (Mb)
#> Ncells  691309 37.0    1291022 69.0         NA   1030820  55.1
#> Vcells 1673131 12.8   10432464 79.6      32768 101529869 774.7
#> > pryr::mem_used()
#> 52.1 MB
#> > memuse::Sys.procmem()
#> Size:  2.670 GiB 
#> > gc()
#>           used (Mb) gc trigger (Mb) limit (Mb)  max used  (Mb)
#> Ncells  691311 37.0    1291022 69.0         NA   1030820  55.1
#> Vcells 1673155 12.8   10432464 79.6      32768 101529869 774.7
#> > pryr::mem_used()
#> 52.1 MB
#> > memuse::Sys.procmem()
#> Size:  2.670 GiB 
#> > gc()
#>           used (Mb) gc trigger (Mb) limit (Mb)  max used  (Mb)
#> Ncells  691313 37.0    1291022 69.0         NA   1030820  55.1
#> Vcells 1673179 12.8   10432464 79.6      32768 101529869 774.7
#> > pryr::mem_used()
#> 52.1 MB
#> > memuse::Sys.procmem()
#> Size:  2.670 GiB 
#> > gc()
#>           used (Mb) gc trigger (Mb) limit (Mb)  max used  (Mb)
#> Ncells  691315 37.0    1291022 69.0         NA   1030820  55.1
#> Vcells 1673203 12.8   10432464 79.6      32768 101529869 774.7
#> > pryr::mem_used()
#> 52.1 MB
#> > memuse::Sys.procmem()
#> Size:  2.670 GiB

schloerke avatar Oct 10 '19 16:10 schloerke

So it should be a more general issue about how OS manages the memory? As of Docker containers, There're options to constrain the memory usage of a Docker container and it may help in such cases.

shrektan avatar Oct 10 '19 16:10 shrektan

@shrektan I don't think limiting the memory for docker containers is the right way to go, as this disables the possibility to process large datasets.

@schloerke Can you further explain this for me?

Also... Linux does not reclaim memory from a process. Docker is running linux. Therefore once the memory is obtained by the R process, it'll never leave.

LukasK13 avatar Oct 11 '19 13:10 LukasK13

Well, you can set soft limits on it which will allow large datasets while may does the trick.

shrektan avatar Oct 11 '19 14:10 shrektan

Hi here. It looks like the issue is related to how glibc manages memory. And this is usually linux specific. More details here. Also I've reported this to r-bugzilla some time ago.

I would appreciate if some of you guys (especially @wch as you are very experienced in r-internals) can review my bug report and confirm it to r-core (as such things were recently requested in r-developer blog)

As a solution I recommend to switch to another implementation of malloc (such as jemalloc). This is exactly what we do in RestRserve:

RUN apt-get update && \
    apt-get install -y --no-install-recommends \
    libssl-dev libjemalloc-dev


ENV LD_PRELOAD /usr/lib/x86_64-linux-gnu/libjemalloc.so

dselivanov avatar Oct 13 '19 09:10 dselivanov

Wow this actually fixes the bug. Thank you for looking into this and sharing the information. I would definitely consider this a bug in R-core! How can I help you in getting this fixed (I don't have an bugzilla account)?

LukasK13 avatar Oct 14 '19 08:10 LukasK13

How can I help you in getting this fixed (I don't have an bugzilla account)

@LukasK13 Could you please create one and drop a comment in the issue?

dselivanov avatar Oct 17 '19 12:10 dselivanov

@dselivanov I think that you understand this domain better than I do -- I don't feel that I would be able to help here. Have you also tried asking about the memory issue on the r-devel mailing list?

wch avatar Oct 21 '19 21:10 wch

Yes, I've opened bugzilla report after discussion on r devel. Links to r devel threads are in the ticket.

dselivanov avatar Oct 22 '19 04:10 dselivanov

@dselivanov how can I create a bugzilla account? I can't find a page for signing up.

LukasK13 avatar Oct 22 '19 15:10 LukasK13

@LukasK13

If you do not already have a bugzilla account send an e-mail (from the address you want to use as your login) to [email protected] briefly explaining why, and a volunteer will add you to R’s Bugzilla members.

dselivanov avatar Oct 22 '19 15:10 dselivanov

The topic of this issue has wandered a little, but on the subject of experimenting with R memory usage it might also be worth investigating the MALLOC_ARENA_MAX environment variable, which seems to improve memory fragmentation with certain kinds of Ruby programs.

atheriel avatar Oct 22 '19 22:10 atheriel

@atheriel I think I've tried all those environment variables at some point. No one of them seem had any effect.

dselivanov avatar Oct 24 '19 06:10 dselivanov

Closing this issue in favor of solution provided by @dselivanov in https://github.com/rstudio/plumber/issues/496#issuecomment-541402503

It seems really fair that the standard memory leak is due to a inefficient malloc linux library. Changing the library had good success according to the comments.

schloerke avatar Nov 17 '23 15:11 schloerke