Caching on multiple keys combined with debouncing ignores first cache key.

I am trying to combine caching of plots alongside debouncing the input data. I have an app that tests this, with the first graph being cached, and the second one not being cached. Unfortunately, when I've tried doing so, the following problem occurs:

If I change the first input "y range", the first graph does not update, while the second one does. If I change the second or third checkbox inputs, then both graphs update. It appears as if the first checkbox input is not being used as a cache key, despite being explicitly included in both the bindCache() call and the shiny:::extractCacheHint() call.

Am I going about this incorrectly, or is this a bug?

library(shiny)
library(tidyverse)

ui <- fluidPage(
  titlePanel('Example Title'),
  sidebarLayout(
    sidebarPanel(
      checkboxGroupInput(
        'yFilterCheckbox',
        label='Filter Checkbox',
        choices = c('y<=-1','-1<y<1','y>=1'),
        selected = c('y<=-1','-1<y<1','y>=1'),
      ),
      checkboxGroupInput(
        'cat1FilterCheckbox',
        label='Category 1 Filter',
        choices=LETTERS[1:4],
        selected=LETTERS[1:4]
      ),
      checkboxGroupInput(
        'cat2FilterCheckbox',
        label='Category 2 Filter',
        choices=c('BIG','SMALL'),
        selected = c('BIG','SMALL')
      )
    ),
    mainPanel(
      plotOutput('myPlot1'),
      plotOutput('myPlot2')
    )
  )
)

server <- function(input, output){
  library(tidyverse)
  set.seed(2021*12*21)
  N=100000
  mydata = data.frame(
    x1 = rnorm(N),
    x2 = rnorm(N),
    x3 = rnorm(N),
    c1 = sample(LETTERS[1:4], N, replace=T),
    c2 = sample(c('BIG','SMALL'), N, replace=T)
  ) %>%
    mutate(
      y1 = x1 * (1 + (c1 == 'D')) + 
        x2 * (1 - (c1 == 'C')) +
        (x3 - rnorm(N)) * (1 + 3 * (c2 == 'BIG')),
      z1 = 1 * (y1 > 0)
    ) 
  # reactives
  
  yFilterCheckbox_r <- reactive(
    input$yFilterCheckbox
  )
  yFilterCheckbox_d = yFilterCheckbox_r %>%
    debounce(1000)
  
  cat1FilterCheckbox_r <- reactive(
    input$cat1FilterCheckbox
  )
  cat1FilterCheckbox_d <- cat1FilterCheckbox_r %>%
    debounce(1000)
  
  cat2FilterCheckbox_r <- reactive(
    input$cat2FilterCheckbox
  )
  cat2FilterCheckbox_d <- cat2FilterCheckbox_r %>%
    debounce(1000)

  filtered_data <- reactive({
    print('filtering...')
    mydata %>%
      filter(
        c1 %in% cat1FilterCheckbox_d() &
          c2 %in% cat2FilterCheckbox_d() &
          case_when(
            y1 <= -1 ~ 'y<=-1' %in% yFilterCheckbox_d(),
            y1 > -1 & y1 < 1 ~ '-1<y<1' %in% yFilterCheckbox_d(),
            TRUE ~ 'y>=1' %in% yFilterCheckbox_d()
          )
      )
  })
    
  output_myPlot1 <- reactive({
    print('plot 1')
    ggplot(
      filtered_data()
    ) +
      geom_point(
        aes(x=x1,y=y1, color=c1, shape=c2)
      ) + theme_bw()
  }) %>%
    bindCache(
      input$yFilterCheckbox,
      input$cat1FilterCheckbox,
      input$cat2FilterCheckbox
    )
  
  output_myPlot2 <- reactive({
    print('plot 2')
    ggplot(
      filtered_data()
    ) +
      geom_point(
        aes(x=x2,y=y1, color=c1, shape=c2)
      ) + theme_bw()
  })
  
  shiny:::extractCacheHint(output_myPlot1)
  
  output$myPlot1 <- renderPlot({output_myPlot1()})
  output$myPlot2 <- renderPlot({output_myPlot2()})
}

shinyApp(ui=ui, server=server)

The debounce() is what threw a wrinkle into the system...

Since filtered_data is created from yFiltercheckbox_d(), cat1FilterCheckbox_d(), and cat2FilterCheckbox_d(), then your bindCache call should also contain these values instead of the non-debounced values.

Ex:

  output_myPlot1 <- reactive({
    print('plot 1')
    ggplot(
      filtered_data()
    ) +
      geom_point(
        aes(x=x1,y=y1, color=c1, shape=c2)
      ) + theme_bw()
  }) %>%
    bindCache(
      yFilterCheckbox_d(),
      cat1FilterCheckbox_d(),
      cat2FilterCheckbox_d()
    )

Making this change causes output_myPlot1 to work as expected.


When the input$yFilterCheckbox value was changed, its value is updated 1 second before the filtered_data() value. During this one second wait time, the updated input$yFilterCheckbox was poisoning the cache with an unexpected plot value.

1 Like

This topic was automatically closed 54 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.