R/cache.R
Cache.Rd
A function that can be used to wrap around other functions to cache function calls
for later use. This is normally most effective when the function to cache is
slow to run, yet the inputs and outputs are small. The benefit of caching, therefore,
will decline when the computational time of the "first" function call is fast and/or
the argument values and return objects are large. The default setting (and first
call to Cache) will always save to disk. The 2nd call to the same function will return
from disk, unless options("reproducible.useMemoise" = TRUE)
, then the 2nd time
will recover the object from RAM and is normally much faster (at the expense of RAM use).
Cache(
FUN,
...,
notOlderThan = NULL,
.objects = NULL,
.cacheExtra = NULL,
.functionName = NULL,
outputObjects = NULL,
algo = "xxhash64",
cacheRepo = NULL,
cachePath = NULL,
length = getOption("reproducible.length", Inf),
compareRasterFileLength,
userTags = c(),
omitArgs = NULL,
classOptions = list(),
debugCache = character(),
sideEffect = FALSE,
makeCopy = FALSE,
quick = getOption("reproducible.quick", FALSE),
verbose = getOption("reproducible.verbose", 1),
cacheId = NULL,
useCache = getOption("reproducible.useCache", TRUE),
useCloud = FALSE,
cloudFolderID = NULL,
showSimilar = getOption("reproducible.showSimilar", FALSE),
drv = getDrv(getOption("reproducible.drv", NULL)),
conn = getOption("reproducible.conn", NULL)
)
Either a function (e.g., rnorm
), a function call (e.g., rnorm(1)
),
or an unevaluated function call (e.g., using
quote
).
Arguments passed to FUN
, if FUN
is not an expression.
A time. Load an object from the Cache if it was created after this.
Character vector of objects to be digested. This is only applicable if there is a list, environment (or similar) with named objects within it. Only this/these objects will be considered for caching, i.e., only use a subset of the list, environment or similar objects. In the case of nested list-type objects, this will only be applied outermost first.
A an arbitrary R object that will be included in the CacheDigest
,
but otherwise not passed into the FUN
. If the user supplies a named list, then
Cache
will report which individual elements of .cacheExtra
have changed
when options("reproducible.showSimilar" = TRUE)
. This can allow a user
more control and understanding for debugging.
A an arbitrary character string that provides a name that is different
than the actual function name (e.g., "rnorm") which will be used for messaging. This
can be useful when the actual function is not helpful for a user, such as do.call
.
Optional character vector indicating which objects to return. This is only relevant for list, environment (or similar) objects
The algorithms to be used; currently available choices are
md5
, which is also the default, sha1
, crc32
,
sha256
, sha512
, xxhash32
, xxhash64
,
murmur32
, spookyhash
, blake3
, crc32c
,
xxh3_64
, and xxh3_128
.
Same as cachePath
, but kept for backwards compatibility.
A repository used for storing cached objects.
This is optional if Cache
is used inside a SpaDES module.
Numeric. If the element passed to Cache is a Path
class
object (from e.g., asPath(filename)
) or it is a Raster
with
file-backing, then this will be
passed to digest::digest
, essentially limiting the number of bytes
to digest (for speed). This will only be used if quick = FALSE
.
Default is getOption("reproducible.length")
, which is set to Inf
.
Being deprecated; use length
.
A character vector with descriptions of the Cache function call. These
will be added to the Cache so that this entry in the Cache can be found using
userTags
e.g., via showCache()
.
Optional character string of arguments in the FUN to omit from the digest.
Optional list. This will pass into .robustDigest
for
specific classes. Should be options that the .robustDigest
knows what
to do with.
Character or Logical. Either "complete"
or "quick"
(uses
partial matching, so "c" or "q" work). TRUE
is equivalent to "complete"
.
If "complete"
, then the returned object from the Cache
function will have two attributes, debugCache1
and debugCache2
,
which are the entire list(...)
and that same object, but after all
.robustDigest
calls, at the moment that it is digested using
digest
, respectively. This attr(mySimOut, "debugCache2")
can then be compared to a subsequent call and individual items within
the object attr(mySimOut, "debugCache1")
can be compared.
If "quick"
, then it will return the same two objects directly,
without evalutating the FUN(...)
.
Now deprecated. Logical or path. Determines where the function will look for new files following function completion. See Details. NOTE: this argument is experimental and may change in future releases.
Now deprecated. Ignored if used.
Logical or character. If TRUE
,
no disk-based information will be assessed, i.e., only
memory content. See Details section about quick
in Cache()
.
Numeric, -1 silent (where possible), 0 being very quiet,
1 showing more messaging, 2 being more messaging, etc.
Default is 1. Above 3 will output much more information about the internals of
Caching, which may help diagnose Caching challenges. Can set globally with an
option, e.g., options('reproducible.verbose' = 0) to reduce to minimal
Character string. If passed, this will override the calculated hash
of the inputs, and return the result from this cacheId in the cachePath
.
Setting this is equivalent to manually saving the output of this function, i.e.,
the object will be on disk, and will be recovered in subsequent
This may help in some particularly finicky situations
where Cache is not correctly detecting unchanged inputs. This will guarantee
the object will be identical each time; this may be useful in operational code.
Logical, numeric or "overwrite"
or "devMode"
. See details.
Logical. See Details.
A googledrive dribble of a folder, e.g., using drive_mkdir()
.
If left as NULL
, the function will create a cloud folder with name from last
two folder levels of the cachePath
path, :
paste0(basename(dirname(cachePath)), "_", basename(cachePath))
.
This cloudFolderID
will be added to options("reproducible.cloudFolderID")
,
but this will not persist across sessions. If this is a character string, it will
treat this as a folder name to create or use on GoogleDrive.
A logical or numeric. Useful for debugging.
If TRUE
or 1
, then if the Cache
does not find an identical archive in the cachePath
, it will report (via message)
the next most recent similar archive, and indicate which argument(s) is/are different.
If a number larger than 1
, then it will report the N most recent similar archived
objects.
if using a database backend, drv must be an object that inherits from DBIDriver e.g., from package RSQLite, e.g., SQLite
an optional DBIConnection object, as returned by dbConnect().
Returns the value of the function call or the cached version (i.e., the result from a previous call to this same cached function with identical arguments).
There are other similar functions in the R universe.
This version of Cache has been used as part of a robust continuous workflow approach.
As a result, we have tested it with many "non-standard" R objects (e.g., RasterLayer
,
Spat*
objects) and environments (which are always unique, so do not cache readily).
This version of the Cache
function accommodates those four special,
though quite common, cases by:
converting any environments into list equivalents;
identifying the dispatched S4 method (including those made through inheritance) before hashing so the correct method is being cached;
by hashing the linked file, rather than the raster object.
Currently, only file-backed Raster*
or Spat*
objects are digested
(e.g., not ff
objects, or any other R object where the data
are on disk instead of in RAM);
Uses digest::digest()
This is used for file-backed objects as well.
Cache will save arguments passed by user in a hidden environment. Any nested Cache functions will use arguments in this order: 1) actual arguments passed at each Cache call; 2) any inherited arguments from an outer Cache call; 3) the default values of the Cache function. See section on Nested Caching.
Cache
will add a tag to the entry in the cache database called accessed
,
which will assign the time that it was accessed, either read or write.
That way, cached items can be shown (using showCache
) or removed (using
clearCache
) selectively, based on their access dates, rather than only
by their creation dates. See example in clearCache()
.
As indicated above, several objects require pre-treatment before
caching will work as expected. The function .robustDigest
accommodates this.
It is an S4 generic, meaning that developers can produce their own methods for
different classes of objects. Currently, there are methods for several types
of classes. See .robustDigest()
.
Commonly, Caching is nested, i.e., an outer function is wrapped in a Cache
function call, and one or more inner functions are also wrapped in a Cache
function call. A user can always specify arguments in every Cache function
call, but this can get tedious and can be prone to errors. The normal way that
R handles arguments is it takes the user passed arguments if any, and
default arguments for all those that have no user passed arguments. We have inserted
a middle step. The order or precedence for any given Cache
function call is
user arguments, 2. inherited arguments, 3. default arguments. At this time,
the top level Cache
arguments will propagate to all inner functions unless
each individual Cache
call has other arguments specified, i.e., "middle"
nested Cache
function calls don't propagate their arguments to further "inner"
Cache
function calls. See example.
userTags
is unique of all arguments: its values will be appended to the
inherited userTags
.
The quick
argument is attempting to sort out an ambiguity with character strings:
are they file paths or are they simply character strings. When quick = TRUE
,
Cache
will treat these as character strings; when quick = FALSE
,
they will be attempted to be treated as file paths first; if there is no file, then
it will revert to treating them as character strings. If user passes a
character vector to this, then this will behave like omitArgs
:
quick = "file"
will treat the argument "file"
as character string.
The most often encountered situation where this ambiguity matters is in arguments about
filenames: is the filename an input pointing to an object whose content we want to
assess (e.g., a file-backed raster), or an output (as in saveRDS) and it should not
be assessed. If only run once, the output file won't exist, so it will be treated
as a character string. However, once the function has been run once, the output file
will exist, and Cache(...)
will assess it, which is incorrect. In these cases,
the user is advised to use quick = "TheOutputFilenameArgument"
to
specify the argument whose content on disk should not be assessed, but whose
character string should be assessed (distinguishing it from omitArgs = "TheOutputFilenameArgument"
, which will not assess the file content nor the
character string).
This is relevant for objects of class character
, Path
and
Raster
currently. For class character
, it is ambiguous whether
this represents a character string or a vector of file paths. If it is known
that character strings should not be treated as paths, then quick = TRUE
is appropriate, with no loss of information. If it is file or
directory, then it will digest the file content, or basename(object)
.
For class Path
objects, the file's metadata (i.e., filename and file
size) will be hashed instead of the file contents if quick = TRUE
. If
set to FALSE
(default), the contents of the file(s) are hashed. If
quick = TRUE
, length
is ignored. Raster
objects are
treated as paths, if they are file-backed.
Caching speed may become a critical aspect of a final product. For example,
if the final product is a shiny app, rerunning the entire project may need
to take less then a few seconds at most. There are 3 arguments that affect
Cache speed: quick
, length
, and
algo
. quick
is passed to .robustDigest
, which currently
only affects Path
and Raster*
class objects. In both cases, quick
means that little or no disk-based information will be assessed.
If a function has a path argument, there is some ambiguity about what should be done. Possibilities include:
hash the string as is (this will be very system specific, meaning a
Cache
call will not work if copied between systems or directories);
hash the basename(path)
;
hash the contents of the file.
If paths are passed in as is (i.e,. character string), the result will not be predictable.
Instead, one should use the wrapper function asPath(path)
, which sets the
class of the string to a Path
, and one should decide whether one wants
to digest the content of the file (using quick = FALSE
),
or just the filename ((quick = TRUE)
). See examples.
In general, it is expected that caching will only be used when randomness is not
desired, e.g., Cache(rnorm(1))
is unlikely to be useful in many cases. However,
Cache
captures the call that is passed to it, leaving all functions unevaluated.
As a result Cache(glm, x ~ y, rnorm(1))
will not work as a means of forcing
a new evaluation each time, as the rnorm(1)
is not evaluated before the call
is assessed against the cache database. To force a new call each time, evaluate
the randomness prior to the Cache call, e.g., ran = rnorm(1)
then pass this
to .cacheExtra
, e.g., Cache(glm, x ~ y, .cacheExtra = ran)
drv
and conn
By default, drv
uses an SQLite database. This can be sufficient for most cases.
However, if a user has dozens or more cores making requests to the Cache database,
it may be insufficient. A user can set up a different database backend, e.g.,
PostgreSQL that can handle multiple simultaneous read-write situations. See
https://github.com/PredictiveEcology/SpaDES/wiki/Using-alternate-database-backends-for-Cache.
useCache
Logical or numeric. If FALSE
or 0
, then the entire Caching
mechanism is bypassed and the
function is evaluated as if it was not being Cached. Default is
getOption("reproducible.useCache")
), which is TRUE
by default,
meaning use the Cache mechanism. This may be useful to turn all Caching on or
off in very complex scripts and nested functions. Increasing levels of numeric
values will cause deeper levels of Caching to occur (though this may not
work as expected in all cases). The following is no longer supported:
Currently, only implemented
in postProcess
: to do both caching of inner cropInputs
, projectInputs
and maskInputs
, and caching of outer postProcess
, use
useCache = 2
; to skip the inner sequence of 3 functions, use useCache = 1
.
For large objects, this may prevent many duplicated save to disk events.
If useCache = "overwrite"
(which can be set with options("reproducible.useCache" = "overwrite")
), then the function invoke the caching mechanism but will purge
any entry that is matched, and it will be replaced with the results of the
current call.
If useCache = "devMode"
: The point of this mode is to facilitate using the Cache when
functions and datasets are continually in flux, and old Cache entries are
likely stale very often. In devMode
, the cache mechanism will work as
normal if the Cache call is the first time for a function OR if it
successfully finds a copy in the cache based on the normal Cache mechanism.
It differs from the normal Cache if the Cache call does not find a copy
in the cachePath
, but it does find an entry that matches based on
userTags
. In this case, it will delete the old entry in the cachePath
(identified based on matching userTags
), then continue with normal Cache
.
For this to work correctly, userTags
must be unique for each function call.
This should be used with caution as it is still experimental. Currently, if
userTags
are not unique to a single entry in the cachePath, it will
default to the behaviour of useCache = TRUE
with a message. This means
that "devMode"
is most useful if used from the start of a project.
useCloud
This is experimental and there are many conditions under which this is known
to not work correctly. This is a way to store all or some of the local Cache in the cloud.
Currently, the only cloud option is Google Drive, via googledrive.
For this to work, the user must be or be able to be authenticated
with googledrive::drive_auth
. The principle behind this
useCloud
is that it will be a full or partial mirror of a local Cache.
It is not intended to be used independently from a local Cache. To share
objects that are in the Cloud with another person, it requires 2 steps. 1)
share the cloudFolderID$id
, which can be retrieved by
getOption("reproducible.cloudFolderID")$id
after at least one Cache
call has been made. 2) The other user must then set their cacheFolderID
in a
Cache\(..., reproducible.cloudFolderID = \"the ID here\"\)
call or
set their option manually
options\(\"reproducible.cloudFolderID\" = \"the ID here\"\)
.
If TRUE
, then this Cache call will download
(if local copy doesn't exist, but cloud copy does exist), upload
(local copy does or doesn't exist and
cloud copy doesn't exist), or
will not download nor upload if object exists in both. If TRUE
will be at
least 1 second slower than setting this to FALSE
, and likely even slower as the
cloud folder gets large. If a user wishes to keep "high-level" control, set this to
getOption("reproducible.useCloud", FALSE)
or
getOption("reproducible.useCloud", TRUE)
(if the default behaviour should
be FALSE
or TRUE
, respectively) so it can be turned on and off with
this option. NOTE: This argument will not be passed into inner/nested Cache calls.)
Users should be cautioned that object attributes may not be preserved, especially
in the case of objects that are file-backed, such as Raster
or SpatRaster
objects.
If a user needs to keep attributes, they may need to manually re-attach them to
the object after recovery. With the example of SpatRaster
objects, saving
to disk requires terra::wrap
if it is a memory-backed object. When running
terra::unwrap
on this object, any attributes that a user had added are lost.
sideEffect
This feature is now deprecated. Do not use as it is ignored.
showCache()
, clearCache()
, keepCache()
,
CacheDigest()
to determine the digest of a given function or expression,
as used internally within Cache
, movedCache()
, .robustDigest()
, and
for more advanced uses there are several helper functions,
e.g., rmFromCache()
, CacheStorageDir()
data.table::setDTthreads(2)
tmpDir <- file.path(tempdir())
opts <- options(reproducible.cachePath = tmpDir)
# Usage -- All below are equivalent; even where args are missing or provided,
# Cache evaluates using default values, if these are specified in formals(FUN)
a <- list()
b <- list(fun = rnorm)
bbb <- 1
ee <- new.env(parent = emptyenv())
ee$qq <- bbb
a[[1]] <- Cache(rnorm(1)) # no evaluation prior to Cache
#> No cachePath supplied and getOption('reproducible.cachePath') is inside a temporary directory;
#> this will not persist across R sessions.
#> Saved! Cache file: 422bae4ed2f770cc.rds; fn: rnorm
a[[2]] <- Cache(rnorm, 1) # no evaluation prior to Cache
#> No cachePath supplied and getOption('reproducible.cachePath') is inside a temporary directory;
#> this will not persist across R sessions.
#> Object to retrieve (fn: rnorm, 422bae4ed2f770cc.rds) ...
#> Loaded! Cached result from previous rnorm call
a[[3]] <- Cache(do.call, rnorm, list(1))
#> No cachePath supplied and getOption('reproducible.cachePath') is inside a temporary directory;
#> this will not persist across R sessions.
#> Object to retrieve (fn: rnorm, 422bae4ed2f770cc.rds) ...
#> Loaded! Cached result from previous rnorm call
a[[4]] <- Cache(do.call(rnorm, list(1)))
#> No cachePath supplied and getOption('reproducible.cachePath') is inside a temporary directory;
#> this will not persist across R sessions.
#> Object to retrieve (fn: rnorm, 422bae4ed2f770cc.rds) ...
#> Loaded! Cached result from previous rnorm call
a[[5]] <- Cache(do.call(b$fun, list(1)))
#> No cachePath supplied and getOption('reproducible.cachePath') is inside a temporary directory;
#> this will not persist across R sessions.
#> Object to retrieve (fn: b$fun, 422bae4ed2f770cc.rds) ...
#> Loaded! Cached result from previous b$fun call
a[[6]] <- Cache(do.call, b$fun, list(1))
#> No cachePath supplied and getOption('reproducible.cachePath') is inside a temporary directory;
#> this will not persist across R sessions.
#> Object to retrieve (fn: b$fun, 422bae4ed2f770cc.rds) ...
#> Loaded! Cached result from previous b$fun call
a[[7]] <- Cache(b$fun, 1)
#> No cachePath supplied and getOption('reproducible.cachePath') is inside a temporary directory;
#> this will not persist across R sessions.
#> Object to retrieve (fn: b$fun, 422bae4ed2f770cc.rds) ...
#> Loaded! Cached result from previous b$fun call
a[[8]] <- Cache(b$fun(1))
#> No cachePath supplied and getOption('reproducible.cachePath') is inside a temporary directory;
#> this will not persist across R sessions.
#> Object to retrieve (fn: $, 422bae4ed2f770cc.rds) ...
#> Loaded! Cached result from previous $ call
a[[10]] <- Cache(quote(rnorm(1)))
#> No cachePath supplied and getOption('reproducible.cachePath') is inside a temporary directory;
#> this will not persist across R sessions.
#> Object to retrieve (fn: quote, 422bae4ed2f770cc.rds) ...
#> Loaded! Cached result from previous quote call
a[[11]] <- Cache(stats::rnorm(1))
#> No cachePath supplied and getOption('reproducible.cachePath') is inside a temporary directory;
#> this will not persist across R sessions.
#> Object to retrieve (fn: stats::rnorm, 422bae4ed2f770cc.rds) ...
#> Loaded! Cached result from previous stats::rnorm call
a[[12]] <- Cache(stats::rnorm, 1)
#> No cachePath supplied and getOption('reproducible.cachePath') is inside a temporary directory;
#> this will not persist across R sessions.
#> Object to retrieve (fn: stats::rnorm, 422bae4ed2f770cc.rds) ...
#> Loaded! Cached result from previous stats::rnorm call
a[[13]] <- Cache(rnorm(1, 0, get("bbb", inherits = FALSE)))
#> No cachePath supplied and getOption('reproducible.cachePath') is inside a temporary directory;
#> this will not persist across R sessions.
#> Object to retrieve (fn: rnorm, 422bae4ed2f770cc.rds) ...
#> Loaded! Cached result from previous rnorm call
a[[14]] <- Cache(rnorm(1, 0, get("qq", inherits = FALSE, envir = ee)))
#> No cachePath supplied and getOption('reproducible.cachePath') is inside a temporary directory;
#> this will not persist across R sessions.
#> Object to retrieve (fn: rnorm, 422bae4ed2f770cc.rds) ...
#> Loaded! Cached result from previous rnorm call
a[[15]] <- Cache(rnorm(1, bbb - bbb, get("bbb", inherits = FALSE)))
#> No cachePath supplied and getOption('reproducible.cachePath') is inside a temporary directory;
#> this will not persist across R sessions.
#> Object to retrieve (fn: rnorm, 422bae4ed2f770cc.rds) ...
#> Loaded! Cached result from previous rnorm call
a[[16]] <- Cache(rnorm(sd = 1, 0, n = get("bbb", inherits = FALSE))) # change order
#> No cachePath supplied and getOption('reproducible.cachePath') is inside a temporary directory;
#> this will not persist across R sessions.
#> Object to retrieve (fn: rnorm, 422bae4ed2f770cc.rds) ...
#> Loaded! Cached result from previous rnorm call
a[[17]] <- Cache(rnorm(1, sd = get("ee", inherits = FALSE)$qq), mean = 0)
#> No cachePath supplied and getOption('reproducible.cachePath') is inside a temporary directory;
#> this will not persist across R sessions.
#> Object to retrieve (fn: rnorm, 422bae4ed2f770cc.rds) ...
#> Loaded! Cached result from previous rnorm call
# with base pipe -- this is put in quotes ('') because R version 4.0 can't understand this
# if you are using R >= 4.1 or R >= 4.2 if using the _ placeholder,
# then you can just use pipe normally
usingPipe1 <- "b$fun(1) |> Cache()" # base pipe
# For long pipe, need to wrap sequence in { }, or else only last step is cached
usingPipe2 <-
'{"bbb" |>
parse(text = _) |>
eval() |>
rnorm()} |>
Cache()'
if (getRversion() >= "4.1") {
a[[9]] <- eval(parse(text = usingPipe1)) # recovers cached copy
}
#> No cachePath supplied and getOption('reproducible.cachePath') is inside a temporary directory;
#> this will not persist across R sessions.
#> Object to retrieve (fn: $, 422bae4ed2f770cc.rds) ...
#> Loaded! Cached result from previous $ call
if (getRversion() >= "4.2") { # uses the _ placeholder; only available in R >= 4.2
a[[18]] <- eval(parse(text = usingPipe2)) # recovers cached copy
}
#> There is an `eval` call in a chain of calls for Cache;
#> eval is evaluated before Cache which may be undesired.
#> Perhaps use `do.call` if the evaluation should not occur prior to Cache
#> No cachePath supplied and getOption('reproducible.cachePath') is inside a temporary directory;
#> this will not persist across R sessions.
#> Object to retrieve (fn: rnorm, 422bae4ed2f770cc.rds) ...
#> Loaded! Cached result from previous rnorm call
length(unique(a)) == 1 # all same
#> [1] FALSE
### Pipe -- have to use { } or else only final function is Cached
if (getRversion() >= "4.1") {
b1a <- 'sample(1e5, 1) |> rnorm() |> Cache()'
b1b <- 'sample(1e5, 1) |> rnorm() |> Cache()'
b2a <- '{sample(1e5, 1) |> rnorm()} |> Cache()'
b2b <- '{sample(1e5, 1) |> rnorm()} |> Cache()'
b1a <- eval(parse(text = b1a))
b1b <- eval(parse(text = b1b))
b2a <- eval(parse(text = b2a))
b2b <- eval(parse(text = b2b))
all.equal(b1a, b1b) # Not TRUE because the sample is run first
all.equal(b2a, b2b) # TRUE because of { }
}
#> No cachePath supplied and getOption('reproducible.cachePath') is inside a temporary directory;
#> this will not persist across R sessions.
#> Saved! Cache file: f1649216458bd037.rds; fn: rnorm
#> No cachePath supplied and getOption('reproducible.cachePath') is inside a temporary directory;
#> this will not persist across R sessions.
#> Saved! Cache file: 56715e20e6a0b4fc.rds; fn: rnorm
#> No cachePath supplied and getOption('reproducible.cachePath') is inside a temporary directory;
#> this will not persist across R sessions.
#> Saved! Cache file: 65d6fc4cf6012274.rds; fn: rnorm
#> No cachePath supplied and getOption('reproducible.cachePath') is inside a temporary directory;
#> this will not persist across R sessions.
#> Object to retrieve (fn: rnorm, 65d6fc4cf6012274.rds) ...
#> Loaded! Cached result from previous rnorm call
#> [1] "Attributes: < Component “.Cache”: Component “newCache”: 1 element mismatch >"
#########################
# Advanced examples
#########################
# .cacheExtra -- add something to digest
Cache(rnorm(1), .cacheExtra = "sfessee11") # adds something other than fn args
#> No cachePath supplied and getOption('reproducible.cachePath') is inside a temporary directory;
#> this will not persist across R sessions.
#> Saved! Cache file: 15a06cafd7e0d37e.rds; fn: rnorm
#> [1] -0.9531766
#> attr(,".Cache")
#> attr(,".Cache")$newCache
#> [1] TRUE
#>
#> attr(,"tags")
#> [1] "cacheId:15a06cafd7e0d37e"
#> attr(,"call")
#> [1] ""
Cache(rnorm(1), .cacheExtra = "nothing") # even though fn is same, the extra is different
#> No cachePath supplied and getOption('reproducible.cachePath') is inside a temporary directory;
#> this will not persist across R sessions.
#> Saved! Cache file: b35d37e57ba86333.rds; fn: rnorm
#> [1] -0.3595797
#> attr(,".Cache")
#> attr(,".Cache")$newCache
#> [1] TRUE
#>
#> attr(,"tags")
#> [1] "cacheId:b35d37e57ba86333"
#> attr(,"call")
#> [1] ""
# omitArgs -- remove something from digest (kind of the opposite of .cacheExtra)
Cache(rnorm(2, sd = 1), omitArgs = "sd") # removes one or more args from cache digest
#> No cachePath supplied and getOption('reproducible.cachePath') is inside a temporary directory;
#> this will not persist across R sessions.
#> Saved! Cache file: d1e9b95723b3afa0.rds; fn: rnorm
#> [1] -1.7026927 -0.8775034
#> attr(,".Cache")
#> attr(,".Cache")$newCache
#> [1] TRUE
#>
#> attr(,"tags")
#> [1] "cacheId:d1e9b95723b3afa0"
#> attr(,"call")
#> [1] ""
Cache(rnorm(2, sd = 2), omitArgs = "sd") # b/c sd is not used, this is same as previous
#> No cachePath supplied and getOption('reproducible.cachePath') is inside a temporary directory;
#> this will not persist across R sessions.
#> Object to retrieve (fn: rnorm, d1e9b95723b3afa0.rds) ...
#> Loaded! Cached result from previous rnorm call
#> [1] -1.7026927 -0.8775034
#> attr(,".Cache")
#> attr(,".Cache")$newCache
#> [1] FALSE
#>
#> attr(,"tags")
#> [1] "cacheId:d1e9b95723b3afa0"
#> attr(,"call")
#> [1] ""
# cacheId -- force the use of a digest -- can give undesired consequences
Cache(rnorm(3), cacheId = "k323431232") # sets the cacheId for this call
#> No cachePath supplied and getOption('reproducible.cachePath') is inside a temporary directory;
#> this will not persist across R sessions.
#> cacheId is not same as calculated hash. Manually searching for
#> cacheId:k323431232
#> Saved! Cache file: k323431232.rds; fn: rnorm
#> [1] -0.2223622 -1.5034283 0.5517413
#> attr(,".Cache")
#> attr(,".Cache")$newCache
#> [1] TRUE
#>
#> attr(,"tags")
#> [1] "cacheId:k323431232"
#> attr(,"call")
#> [1] ""
Cache(runif(14), cacheId = "k323431232") # recovers same as above, i.e, rnorm(3)
#> No cachePath supplied and getOption('reproducible.cachePath') is inside a temporary directory;
#> this will not persist across R sessions.
#> cacheId is not same as calculated hash. Manually searching for
#> cacheId:k323431232
#> Object to retrieve (fn: runif, k323431232.rds) ...
#> Loaded! Cached result from previous runif call
#> [1] -0.2223622 -1.5034283 0.5517413
#> attr(,".Cache")
#> attr(,".Cache")$newCache
#> [1] FALSE
#>
#> attr(,"tags")
#> [1] "cacheId:k323431232"
#> attr(,"call")
#> [1] ""
# Turn off Caching session-wide
opts <- options(reproducible.useCache = FALSE)
Cache(rnorm(3)) # doesn't cache
#> useCache is FALSE; skipping Cache on function rnorm (currently running nested
#> Cache level 2)
#> [1] 0.2878185 1.9202443 0.4524596
options(opts)
# showSimilar can help with debugging why a Cache call isn't picking up a cached copy
Cache(rnorm(4), showSimilar = TRUE) # shows that the argument `n` is different
#> No cachePath supplied and getOption('reproducible.cachePath') is inside a temporary directory;
#> this will not persist across R sessions.
#> Cache of 'rnorm' differs from
#> the next closest cacheId(s) k323431232 of 'rnorm'
#> different n
#> Saved! Cache file: ad0ea27476c50b66.rds; fn: rnorm
#> [1] -0.5333021 0.5382077 -0.1320896 -0.2202324
#> attr(,".Cache")
#> attr(,".Cache")$newCache
#> [1] TRUE
#>
#> attr(,"tags")
#> [1] "cacheId:ad0ea27476c50b66"
#> attr(,"call")
#> [1] ""
###############################################
# devMode -- enables cache database to stay
# small even when developing code
###############################################
opt <- options("reproducible.useCache" = "devMode")
clearCache(tmpDir, ask = FALSE)
centralTendency <- function(x) {
mean(x)
}
funnyData <- c(1, 1, 1, 1, 10)
uniqueUserTags <- c("thisIsUnique", "reallyUnique")
ranNumsB <- Cache(centralTendency, funnyData, cachePath = tmpDir,
userTags = uniqueUserTags) # sets new value to Cache
#> Saved! Cache file: 197064fee76dde59.rds; fn: centralTendency
showCache(tmpDir) # 1 unique cacheId -- cacheId is 71cd24ec3b0d0cac
#> Cache size:
#> Total (including Rasters): 246 bytes
#> Selected objects (not including Rasters): 246 bytes
#> cacheId tagKey tagValue
#> <char> <char> <char>
#> 1: 197064fee76dde59 thisIsUnique thisIsUnique
#> 2: 197064fee76dde59 reallyUnique reallyUnique
#> 3: 197064fee76dde59 function centralTendency
#> 4: 197064fee76dde59 class numeric
#> 5: 197064fee76dde59 object.size 984
#> 6: 197064fee76dde59 accessed 2024-12-12 06:15:33.532066
#> 7: 197064fee76dde59 inCloud FALSE
#> 8: 197064fee76dde59 fromDisk FALSE
#> 9: 197064fee76dde59 resultHash
#> 10: 197064fee76dde59 elapsedTimeDigest 0.001622677 secs
#> 11: 197064fee76dde59 elapsedTimeFirstRun 0.0001528263 secs
#> 12: 197064fee76dde59 otherFunctions build_site
#> 13: 197064fee76dde59 otherFunctions build_site_local
#> 14: 197064fee76dde59 otherFunctions build_reference
#> 15: 197064fee76dde59 otherFunctions unwrap_purrr_error
#> 16: 197064fee76dde59 otherFunctions map_
#> 17: 197064fee76dde59 otherFunctions with_indexed_errors
#> 18: 197064fee76dde59 otherFunctions call_with_cleanup
#> 19: 197064fee76dde59 otherFunctions data_reference_topic
#> 20: 197064fee76dde59 otherFunctions run_examples
#> 21: 197064fee76dde59 otherFunctions highlight_examples
#> 22: 197064fee76dde59 otherFunctions with_handlers
#> 23: 197064fee76dde59 preDigest x:e4aa8de28dc6c1bb
#> 24: 197064fee76dde59 preDigest .FUN:3df5c81377ae4909
#> cacheId tagKey tagValue
#> createdDate
#> <char>
#> 1: 2024-12-12 06:15:33.532694
#> 2: 2024-12-12 06:15:33.532694
#> 3: 2024-12-12 06:15:33.532694
#> 4: 2024-12-12 06:15:33.532694
#> 5: 2024-12-12 06:15:33.532694
#> 6: 2024-12-12 06:15:33.532694
#> 7: 2024-12-12 06:15:33.532694
#> 8: 2024-12-12 06:15:33.532694
#> 9: 2024-12-12 06:15:33.532694
#> 10: 2024-12-12 06:15:33.532694
#> 11: 2024-12-12 06:15:33.532694
#> 12: 2024-12-12 06:15:33.532694
#> 13: 2024-12-12 06:15:33.532694
#> 14: 2024-12-12 06:15:33.532694
#> 15: 2024-12-12 06:15:33.532694
#> 16: 2024-12-12 06:15:33.532694
#> 17: 2024-12-12 06:15:33.532694
#> 18: 2024-12-12 06:15:33.532694
#> 19: 2024-12-12 06:15:33.532694
#> 20: 2024-12-12 06:15:33.532694
#> 21: 2024-12-12 06:15:33.532694
#> 22: 2024-12-12 06:15:33.532694
#> 23: 2024-12-12 06:15:33.532694
#> 24: 2024-12-12 06:15:33.532694
#> createdDate
# During development, we often redefine function internals
centralTendency <- function(x) {
median(x)
}
# When we rerun, we don't want to keep the "old" cache because the function will
# never again be defined that way. Here, because of userTags being the same,
# it will replace the entry in the Cache, effetively overwriting it, even though
# it has a different cacheId
ranNumsD <- Cache(centralTendency, funnyData, cachePath = tmpDir, userTags = uniqueUserTags)
#> Overwriting Cache entry with userTags: 'thisIsUnique, reallyUnique,
#> centralTendency'
#> Saved! Cache file: e3afa6e86ce220c2.rds; fn: centralTendency
showCache(tmpDir) # 1 unique artifact -- cacheId is 632cd06f30e111be
#> Cache size:
#> Total (including Rasters): 246 bytes
#> Selected objects (not including Rasters): 246 bytes
#> cacheId tagKey tagValue
#> <char> <char> <char>
#> 1: e3afa6e86ce220c2 thisIsUnique thisIsUnique
#> 2: e3afa6e86ce220c2 reallyUnique reallyUnique
#> 3: e3afa6e86ce220c2 function centralTendency
#> 4: e3afa6e86ce220c2 class numeric
#> 5: e3afa6e86ce220c2 object.size 984
#> 6: e3afa6e86ce220c2 accessed 2024-12-12 06:15:33.586837
#> 7: e3afa6e86ce220c2 inCloud FALSE
#> 8: e3afa6e86ce220c2 fromDisk FALSE
#> 9: e3afa6e86ce220c2 resultHash
#> 10: e3afa6e86ce220c2 elapsedTimeDigest 0.001521587 secs
#> 11: e3afa6e86ce220c2 elapsedTimeFirstRun 0.0001835823 secs
#> 12: e3afa6e86ce220c2 otherFunctions build_site
#> 13: e3afa6e86ce220c2 otherFunctions build_site_local
#> 14: e3afa6e86ce220c2 otherFunctions build_reference
#> 15: e3afa6e86ce220c2 otherFunctions unwrap_purrr_error
#> 16: e3afa6e86ce220c2 otherFunctions map_
#> 17: e3afa6e86ce220c2 otherFunctions with_indexed_errors
#> 18: e3afa6e86ce220c2 otherFunctions call_with_cleanup
#> 19: e3afa6e86ce220c2 otherFunctions data_reference_topic
#> 20: e3afa6e86ce220c2 otherFunctions run_examples
#> 21: e3afa6e86ce220c2 otherFunctions highlight_examples
#> 22: e3afa6e86ce220c2 otherFunctions with_handlers
#> 23: e3afa6e86ce220c2 preDigest x:e4aa8de28dc6c1bb
#> 24: e3afa6e86ce220c2 preDigest .FUN:88d14fe75c352ad5
#> cacheId tagKey tagValue
#> createdDate
#> <char>
#> 1: 2024-12-12 06:15:33.587432
#> 2: 2024-12-12 06:15:33.587432
#> 3: 2024-12-12 06:15:33.587432
#> 4: 2024-12-12 06:15:33.587432
#> 5: 2024-12-12 06:15:33.587432
#> 6: 2024-12-12 06:15:33.587432
#> 7: 2024-12-12 06:15:33.587432
#> 8: 2024-12-12 06:15:33.587432
#> 9: 2024-12-12 06:15:33.587432
#> 10: 2024-12-12 06:15:33.587432
#> 11: 2024-12-12 06:15:33.587432
#> 12: 2024-12-12 06:15:33.587432
#> 13: 2024-12-12 06:15:33.587432
#> 14: 2024-12-12 06:15:33.587432
#> 15: 2024-12-12 06:15:33.587432
#> 16: 2024-12-12 06:15:33.587432
#> 17: 2024-12-12 06:15:33.587432
#> 18: 2024-12-12 06:15:33.587432
#> 19: 2024-12-12 06:15:33.587432
#> 20: 2024-12-12 06:15:33.587432
#> 21: 2024-12-12 06:15:33.587432
#> 22: 2024-12-12 06:15:33.587432
#> 23: 2024-12-12 06:15:33.587432
#> 24: 2024-12-12 06:15:33.587432
#> createdDate
# If it finds it by cacheID, doesn't matter what the userTags are
ranNumsD <- Cache(centralTendency, funnyData, cachePath = tmpDir, userTags = "thisIsUnique")
#> Object to retrieve (fn: centralTendency, e3afa6e86ce220c2.rds) ...
#> Loaded! Cached result from previous centralTendency call
options(opt)
#########################################
# For more in depth uses, see vignette
if (interactive())
browseVignettes(package = "reproducible")