Search code examples
rmemorymemory-leaksshinyobservers

Memory leak by using observeEvent


I get cumulating memory using the following code. Every time I switch between the action button 1 and 2 the used memory increases.

library(ggplot2)
library(shiny)
library(lobstr)

ui <- navbarPage("Test",fluidPage(fluidRow(
                     column(width = 1, actionButton("action_input_1", label = "1")), 
                     column(width = 1, actionButton("action_input_2", label = "2")),
                     column(width = 10, plotOutput("plot", width = 1400, height = 800)))))

server <- function(input, output) {
  # 1
  observeEvent(input$action_input_1, {
    output$plot <- renderPlot({
      plot(rnorm(100))
    })
    print(cat(paste0("mem used 1: ", capture.output(print(mem_used())),"\n")))
  })

  # 2
  observeEvent(input$action_input_2, {
    output$plot <- renderPlot({
      plot(rnorm(1000))
    })
    print(cat(paste0("mem used 2: ", capture.output(print(mem_used())),"\n")))
  })
}
shinyApp(ui, server)

Due to a suggestion in this post, I have tried not to use observeEvent. Here is the server function:

server <- function(input, output) {
  # 1
  output$plot <- renderPlot({
    input$action_input_1
    plot(rnorm(100))
    print(cat(paste0("mem used 1: ", capture.output(print(mem_used())),"\n")))
  })

  # 2
  output$plot <- renderPlot({
    input$action_input_2
    plot(rnorm(1000))
    print(cat(paste0("mem used 2: ", capture.output(print(mem_used())),"\n")))
  })
}

Here the memory does not increase, but only the second action button (=the last block of code?) is working. Is there a solution to prevent memory leak and get both buttons working?


Solution

  • How about using reactiveVal :

    reactiveData <- reactiveVal(NULL)
    observeEvent(input$action_input_1, reactiveData(rnorm(100)))
    observeEvent(input$action_input_2, reactiveData(rnorm(1000)))
    output$plot <- renderPlot(plot(reactiveData()))
    

    Reactive values have a little different syntax:

    reactiveData <- reactiveValues(rnorm = NULL, bool_val = NULL) 
    
    observeEvent(input$action_input_1,  {# reactiveData(rnorm(100), bool_val <- TRUE)) 
       reactiveData$rnorm <- rnorm(100)
       reactiveData$bool_val <- TRUE
    })
    
    observeEvent(input$action_input_2, { #reactiveData(rnorm(1000), bool_val <- FALSE)) 
      reactiveData$rnorm <- rnorm(1000)
      reactiveData$bool_val <-  FALSE
    })
    
    output$plot <- renderPlot(plot(reactiveData$rnorm))
    

    Though your variables are changing in concert, so you could technically still use reactiveVal

    reactiveData <- reactiveVal(list(rnorm = NULL, bool_val = NULL)) 
    
    observeEvent(input$action_input_1, reactiveData(list(rnorm = 100, bool_val = TRUE)))
    
    observeEvent(input$action_input_2, reactiveData(list(rnorm = 1000, bool_val = FALSE)))
    
    output$plot <- renderPlot(plot(reactiveData()$rnorm))