mirror of
https://github.com/rstudio/shiny.git
synced 2026-04-29 03:00:45 -04:00
368 lines
16 KiB
R
368 lines
16 KiB
R
% Generated by roxygen2: do not edit by hand
|
|
% Please edit documentation in R/bind-cache.R
|
|
\name{bindCache}
|
|
\alias{bindCache}
|
|
\title{Add caching with reactivity to an object}
|
|
\usage{
|
|
bindCache(x, ..., cache = "app")
|
|
}
|
|
\arguments{
|
|
\item{x}{The object to add caching to.}
|
|
|
|
\item{...}{One or more expressions to use in the caching key.}
|
|
|
|
\item{cache}{The scope of the cache, or a cache object. This can be \code{"app"}
|
|
(the default), \code{"session"}, or a cache object like a \code{\link[cachem:cache_disk]{cachem::cache_disk()}}.
|
|
See the Cache Scoping section for more information.}
|
|
}
|
|
\description{
|
|
\code{bindCache()} adds caching to the following kinds of objects used in Shiny:
|
|
\itemize{
|
|
\item \code{\link[=reactive]{reactive()}}\verb{expressions. *}render*` functions, like \code{\link[=renderText]{renderText()}},
|
|
\code{\link[=renderTable]{renderTable()}}, and so on.
|
|
}
|
|
|
|
Ordinary \code{\link[=reactive]{reactive()}} expressions will cache their most recent value. This
|
|
can make computation more efficient, because time-consuming code can execute
|
|
once and the result can be used in multiple different places without needing
|
|
to re-execute the code.
|
|
|
|
When \code{bindCache()} is used on a reactive expression, any number of previous
|
|
values can be cache (as long as they fit in the cache). You must provide one
|
|
or more expressions that are used to generate a \emph{cache key}. The \code{...}
|
|
expressions are put together in a list and hashed, and the result is used as
|
|
the cache \strong{key}.
|
|
|
|
There is also a \strong{value} expression, which is the expression passed to the
|
|
original reactive.
|
|
|
|
For each possible \strong{key}, there should be one possible \strong{value} returned by
|
|
the original reactive expression; a given key should not correspond to
|
|
multiple possible values from the reactive expression.
|
|
|
|
To use \code{bindCache()}, the key should be fast to compute, and the value should
|
|
expensive (so that it benefits from caching). To see if the value should be
|
|
computed, a cached reactive evaluates the key, and then serializes and hashes
|
|
the result. If the resulting hashed key is in the cache, then the cached
|
|
reactive simply retrieves the previously calculated value and returns it; if
|
|
not, then the value is computed and the result is stored in the cache before
|
|
being returned.
|
|
|
|
In most cases, the key will contain any reactive inputs that are used by the
|
|
value expression. It is also best to use non-reference objects, since the
|
|
serialization of these objects may not capture relevant changes.
|
|
|
|
\code{bindCache()} is often used in conjunction with \code{\link[=bindEvent]{bindEvent()}}.
|
|
}
|
|
\section{Cache keys and reactivity}{
|
|
|
|
|
|
Because the \strong{value} expression (from the original \code{\link[=reactive]{reactive()}}) is
|
|
cached, it is not necessarily re-executed when someone retrieves a value,
|
|
and therefore it can't be used to decide what objects to take reactive
|
|
dependencies on. Instead, the \strong{key} is used to figure out which objects
|
|
to take reactive dependencies on. In short, the key expression is reactive,
|
|
and value expression is no longer reactive.
|
|
|
|
Here's an example of what not to do: if the key is \code{input$x} and the value
|
|
expression is from \code{reactive({input$x + input$y})}, then the resulting
|
|
cached reactive will only take a reactive dependency on \code{input$x} -- it
|
|
won't recompute \code{{input$x + input$y}} when just \code{input$y} changes.
|
|
Moreover, the cache won't use \code{input$y} as part of the key, and so it could
|
|
return incorrect values in the future when it retrieves values from the
|
|
cache. (See the examples below for an example of this.)
|
|
|
|
A better cache key would be something like \verb{input$x, input$y}. This does
|
|
two things: it ensures that a reactive dependency is taken on both
|
|
\code{input$x} and \code{input$y}, and it also makes sure that both values are
|
|
represented in the cache key.
|
|
|
|
In general, \code{key} should use the same reactive inputs as \code{value}, but the
|
|
computation should be simpler. If there are other (non-reactive) values
|
|
that are consumed, such as external data sources, they should be used in
|
|
the \code{key} as well. Note that if the \code{key} is large, it can make sense to do
|
|
some sort of reduction on it so that the serialization and hashing of the
|
|
cache key is not too expensive.
|
|
|
|
Remember that the key is \emph{reactive}, so it is not re-executed every single
|
|
time that someone accesses the cached reactive. It is only re-executed if
|
|
it has been invalidated by one of the reactives it depends on. For
|
|
example, suppose we have this cached reactive:\if{html}{\out{<div class="NA">}}\preformatted{r <- reactive(\{ input$x + input$y \}) \%>\%
|
|
bindCache(input$x, input$y)
|
|
}\if{html}{\out{</div>}}
|
|
|
|
In this case, the key expression is essentially \code{reactive(list(input$x, input$y))} (there's a bit more to it, but that's a good enough
|
|
approximation). The first time \code{r()} is called, it executes the key, then
|
|
fails to find it in the cache, so it executes the value expression, \code{{ input$x + input$y }}. If \code{r()} is called again, then it does not need to
|
|
re-execute the key expression, because it has not been invalidated via a
|
|
change to \code{input$x} or \code{input$y}; it simply returns the previous value.
|
|
However, if \code{input$x} or \code{input$y} changes, then the reactive expression will
|
|
be invalidated, and the next time that someone calls \code{r()}, the key
|
|
expression will need to be re-executed.
|
|
|
|
Note that if the cached reactive is passed to \code{\link[=bindEvent]{bindEvent()}}, then the key
|
|
expression will no longer be reactive; instead, the event expression will be
|
|
reactive.
|
|
}
|
|
|
|
\section{Async with cached reactives}{
|
|
|
|
|
|
With a cached reactive expression, the key and/or value expression can be
|
|
\emph{asynchronous}. In other words, they can be promises --- not regular R
|
|
promises, but rather objects provided by the
|
|
\href{https://rstudio.github.io/promises/}{\pkg{promises}} package, which
|
|
are similar to promises in JavaScript. (See \code{\link[promises:promise]{promises::promise()}} for more
|
|
information.) You can also use \code{\link[future:future]{future::future()}} objects to run code in a
|
|
separate process or even on a remote machine.
|
|
|
|
If the value returns a promise, then anything that consumes the cached
|
|
reactive must expect it to return a promise.
|
|
|
|
Similarly, if the key is a promise (in other words, if it is asynchronous),
|
|
then the entire cached reactive must be asynchronous, since the key must be
|
|
computed asynchronously before it knows whether to compute the value or the
|
|
value is retrieved from the cache. Anything that consumes the cached
|
|
reactive must therefore expect it to return a promise.
|
|
}
|
|
|
|
\section{Cache scope}{
|
|
|
|
|
|
By default, a cached reactive is scoped to the running application. That
|
|
means that it shares a cache with all user sessions connected to the
|
|
application (within the R process). This is done with the \code{cache}
|
|
parameter's default value, \code{"app"}.
|
|
|
|
With an app-level cache scope, one user can benefit from the work done for
|
|
another user's session. In most cases, this is the best way to get
|
|
performance improvements from caching. However, in some cases, this could
|
|
leak information between sessions. For example, if the cache key does not
|
|
fully encompass the inputs used by the value, then data could leak between
|
|
the sessions. Or if a user sees that a cached reactive returns its value
|
|
very quickly, they may be able to infer that someone else has already used
|
|
it with the same values.
|
|
|
|
It is also possible to scope the cache to the session, with
|
|
\code{cache="session"}. This removes the risk of information leaking between
|
|
sessions, but then one session cannot benefit from computations performed in
|
|
another session.
|
|
|
|
It is possible to pass in caching objects directly to
|
|
\code{bindCache()}. This can be useful if, for example, you want to use a
|
|
particular type of cache with specific cached reactives, or if you want to
|
|
use a \code{\link[cachem:cache_disk]{cachem::cache_disk()}} that is shared across multiple processes and
|
|
persists beyond the current R session.
|
|
|
|
To use different settings for an application-scoped cache, you can call
|
|
\code{\link[=shinyOptions]{shinyOptions()}} at the top of your app.R, server.R, or
|
|
global.R. For example, this will create a cache with 500 MB of space
|
|
instead of the default 200 MB:\preformatted{shinyOptions(cache = cachem::cache_mem(size = 500e6))
|
|
}
|
|
|
|
To use different settings for a session-scoped cache, you can set
|
|
\code{self$cache} at the top of your server function. By default, it will create
|
|
a 200 MB memory cache for each session , but you can replace it with
|
|
something different. To use the session-scoped cache, you must also call
|
|
\code{bindCache()} with \code{cache="session"}. This will create a 100 MB cache for
|
|
the session:\preformatted{function(input, output, session) \{
|
|
session$cache <- cachem::cache_mem(size = 100e6)
|
|
...
|
|
\}
|
|
}
|
|
|
|
If you want to use a cache that is shared across multiple R processes, you
|
|
can use a \code{\link[cachem:cache_disk]{cachem::cache_disk()}}. You can create a application-level shared
|
|
cache by putting this at the top of your app.R, server.R, or global.R:\preformatted{shinyOptions(cache = cachem::cache_disk(file.path(dirname(tempdir()), "myapp-cache"))
|
|
}
|
|
|
|
This will create a subdirectory in your system temp directory named
|
|
\code{myapp-cache} (replace \code{myapp-cache} with a unique name of
|
|
your choosing). On most platforms, this directory will be removed when
|
|
your system reboots. This cache will persist across multiple starts and
|
|
stops of the R process, as long as you do not reboot.
|
|
|
|
To have the cache persist even across multiple reboots, you can create the
|
|
cache in a location outside of the temp directory. For example, it could
|
|
be a subdirectory of the application:\preformatted{shinyOptions(cache = cachem::cache_disk("./myapp-cache"))
|
|
}
|
|
|
|
In this case, resetting the cache will have to be done manually, by deleting
|
|
the directory.
|
|
|
|
You can also scope a cache to just one item, or selected items. To do that,
|
|
create a \code{\link[cachem:cache_mem]{cachem::cache_mem()}} or \code{\link[cachem:cache_disk]{cachem::cache_disk()}}, and pass it
|
|
as the \code{cache} argument of \code{bindCache()}.
|
|
}
|
|
|
|
\section{Cache key internals}{
|
|
|
|
|
|
The actual cache key that is used internally takes value from evaluating the
|
|
key expression(s) and combines it with the (unevaluated) value expression.
|
|
|
|
This means that if there are two cached reactives which have the same result
|
|
from evaluating the key, but different value expressions, then they will not
|
|
need to worry about collisions.
|
|
|
|
However, if two cached reactives have identical key and value expressions
|
|
expressions, they will share the cached values. This is useful when using
|
|
\code{cache="app"}: there may be multiple user sessions which create separate
|
|
cached reactive objects (because they are created from the same code in the
|
|
server function, but the server function is executed once for each user
|
|
session), and those cached reactive objects across sessions can share
|
|
values in the cache.
|
|
}
|
|
|
|
\section{Developing render functions for caching}{
|
|
|
|
|
|
If you write \code{render} functions (for example, \code{renderFoo()}), you may
|
|
need to provide a \code{cacheHint}, so that \code{bindCache()} knows how to correctly
|
|
cache the output.
|
|
|
|
The potential problem is a cache collision. Consider the following:\preformatted{output$x1 <- renderText(\{ input$x \}) \%>\% bindCache(input$x)
|
|
output$x2 <- renderText(\{ input$x * 2 \}) \%>\% bindCache(input$x)
|
|
}
|
|
|
|
Both \code{output$x1} and \code{output$x2} use \code{input$x} as part of their cache key,
|
|
but if it were the only thing used in the cache key, then the two outputs
|
|
would have a cache collision, and they would have the same output. To avoid
|
|
this, a \emph{cache hint} is automatically added when \code{\link[=renderText]{renderText()}} calls
|
|
\code{\link[=createRenderFunction]{createRenderFunction()}}. The cache hint is used as part of the actual
|
|
cache key, in addition to the one passed to \code{bindCache()} by the user. The
|
|
cache hint can be viewed by calling the internal Shiny function
|
|
\code{extractCacheHint()}:\if{html}{\out{<div class="NA">}}\preformatted{r <- renderText(\{ input$x \})
|
|
shiny:::extractCacheHint(r)
|
|
}\if{html}{\out{</div>}}
|
|
|
|
This returns a nested list containing an item, \verb{$origUserFunc$body}, which
|
|
in this case is the expression which was passed to \code{renderText()}:
|
|
\code{{ input$x }}. This (quoted) expression is mixed into the actual cache
|
|
key, and it is how \code{output$x1} does not have collisions with \code{output$x2}.
|
|
|
|
For most developers of render functions, nothing extra needs to be done;
|
|
the automatic inference of the cache hint is sufficient. Again, you can
|
|
check it by calling \code{shiny:::extractCacheHint()}, and by testing the
|
|
render function for cache collisions in a real application.
|
|
|
|
In some cases, however, the automatic cache hint inference is not
|
|
sufficient, and it is necessary to provide a cache hint. This is true
|
|
for \code{renderPrint()}. Unlike \code{renderText()}, it wraps the user-provided
|
|
expression in another function, before passing it to \code{\link[=markRenderFunction]{markRenderFunction()}}
|
|
(instead of \code{\link[=createRenderFunction]{createRenderFunction()}}). Because the user code is wrapped in
|
|
another function, markRenderFunction() is not able to automatically extract
|
|
the user-provided code and use it in the cache key. Instead, \code{renderPrint}
|
|
calls \code{markRenderFunction()}, it explicitly passes along a \code{cacheHint},
|
|
which includes a label and the original user expression.
|
|
}
|
|
|
|
\section{Uncacheable objects}{
|
|
|
|
|
|
Some render functions cannot be cached, typically because they have side
|
|
effects or modify some external state, and they must re-execute each time
|
|
in order to work properly.
|
|
|
|
For developers of such code, they should call \code{\link[=createRenderFunction]{createRenderFunction()}} or
|
|
\code{\link[=markRenderFunction]{markRenderFunction()}} with \code{cacheHint = FALSE}.
|
|
}
|
|
|
|
\examples{
|
|
\dontrun{
|
|
rc <- bindCache(
|
|
x = reactive({
|
|
Sys.sleep(2) # Pretend this is expensive
|
|
input$x * 100
|
|
}),
|
|
input$x
|
|
)
|
|
|
|
# Can make it prettier with the \%>\% operator
|
|
library(magrittr)
|
|
|
|
rc <- reactive({
|
|
Sys.sleep(2)
|
|
input$x * 100
|
|
}) \%>\%
|
|
bindCache(input$x)
|
|
|
|
}
|
|
|
|
## Only run app examples in interactive R sessions
|
|
if (interactive()) {
|
|
|
|
# Basic example
|
|
shinyApp(
|
|
ui = fluidPage(
|
|
sliderInput("x", "x", 1, 10, 5),
|
|
sliderInput("y", "y", 1, 10, 5),
|
|
div("x * y: "),
|
|
verbatimTextOutput("txt")
|
|
),
|
|
server = function(input, output) {
|
|
r <- reactive({
|
|
# The value expression is an _expensive_ computation
|
|
message("Doing expensive computation...")
|
|
Sys.sleep(2)
|
|
input$x * input$y
|
|
}) \%>\%
|
|
bindCache(input$x, input$y)
|
|
|
|
output$txt <- renderText(r())
|
|
}
|
|
)
|
|
|
|
|
|
# Caching renderText
|
|
shinyApp(
|
|
ui = fluidPage(
|
|
sliderInput("x", "x", 1, 10, 5),
|
|
sliderInput("y", "y", 1, 10, 5),
|
|
div("x * y: "),
|
|
verbatimTextOutput("txt")
|
|
),
|
|
server = function(input, output) {
|
|
output$txt <- renderText({
|
|
message("Doing expensive computation...")
|
|
Sys.sleep(2)
|
|
input$x * input$y
|
|
}) \%>\%
|
|
bindCache(input$x, input$y)
|
|
}
|
|
)
|
|
|
|
|
|
# Demo of using events and caching with an actionButton
|
|
shinyApp(
|
|
ui = fluidPage(
|
|
sliderInput("x", "x", 1, 10, 5),
|
|
sliderInput("y", "y", 1, 10, 5),
|
|
actionButton("go", "Go"),
|
|
div("x * y: "),
|
|
verbatimTextOutput("txt")
|
|
),
|
|
server = function(input, output) {
|
|
r <- reactive({
|
|
message("Doing expensive computation...")
|
|
Sys.sleep(2)
|
|
input$x * input$y
|
|
}) \%>\%
|
|
bindCache(input$x, input$y) \%>\%
|
|
bindEvent(input$go)
|
|
# The cached, eventified reactive takes a reactive dependency on
|
|
# input$go, but doesn't use it for the cache key. It uses input$x and
|
|
# input$y for the cache key, but doesn't take a reactive depdency on
|
|
# them, because the reactive dependency is superseded by addEvent().
|
|
|
|
output$txt <- renderText(r())
|
|
}
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
\seealso{
|
|
\code{\link[=bindEvent]{bindEvent()}}, \code{\link[=renderCachedPlot]{renderCachedPlot()}} for caching plots.
|
|
}
|