chat / R /caching.R
WeMWish's picture
Fix infinite loop bug in literature search system
557ed35
# caching.R
# Directory to store cache files
CACHE_DIR <- "./cache_data"
# Ensure cache directory exists
if (!dir.exists(CACHE_DIR)) {
dir.create(CACHE_DIR, recursive = TRUE)
}
#' Generate a cache key from an operation name and its arguments.
#'
#' @param operation_name A string identifying the operation.
#' @param ... Arguments to the operation, used to create a unique hash.
#' @return A string representing the cache key.
generate_cache_key <- function(operation_name, ...) {
# Create a list of arguments
args_list <- list(...)
# Combine operation name and a digest of the arguments
# Ensure consistent ordering of named arguments for consistent hashing
if (length(args_list) > 0) {
if (!is.null(names(args_list))) {
args_list <- args_list[order(names(args_list))]
}
# Use deparse and digest for complex arguments
args_digest <- digest::digest(lapply(args_list, deparse))
key_string <- paste(operation_name, args_digest, sep = "_")
} else {
key_string <- operation_name
}
# Sanitize the key to be a valid filename
key_string <- gsub("[^a-zA-Z0-9_.-]", "_", key_string)
return(paste0(key_string, ".rds"))
}
#' Retrieve an item from the cache.
#'
#' @param key The cache key (typically generated by generate_cache_key).
#' @param max_age_seconds The maximum age of the cache file in seconds.
#' If the cache file is older, it's considered stale.
#' Default is NULL (no age check).
#' @return The cached item, or NULL if not found or stale.
get_cached_item <- function(key, max_age_seconds = NULL) {
cache_file_path <- file.path(CACHE_DIR, key)
if (file.exists(cache_file_path)) {
if (!is.null(max_age_seconds)) {
file_info <- file.info(cache_file_path)
if (difftime(Sys.time(), file_info$mtime, units = "secs") > max_age_seconds) {
# Cache is stale
message(paste("Cache stale for key:", key, "- Recomputing."))
return(NULL)
}
}
message(paste("Cache hit for key:", key))
return(readRDS(cache_file_path))
} else {
message(paste("Cache miss for key:", key))
return(NULL)
}
}
#' Save an item to the cache.
#'
#' @param key The cache key.
#' @param value The item to save.
save_cached_item <- function(key, value) {
if (is.null(value)) {
# Avoid saving NULLs if an operation truly returns NULL
# Or handle as an explicit cache clear if needed
message(paste("Skipping saving NULL value to cache for key:", key))
return()
}
cache_file_path <- file.path(CACHE_DIR, key)
tryCatch({
saveRDS(value, file = cache_file_path)
message(paste("Saved item to cache. Key:", key))
}, error = function(e) {
warning(paste("Error saving item to cache for key:", key, ":", e$message))
})
}
#' Clear the entire cache directory.
clear_all_cache <- function() {
files_in_cache <- list.files(CACHE_DIR, full.names = TRUE)
if (length(files_in_cache) > 0) {
removed_files <- file.remove(files_in_cache)
message(paste("Cleared", sum(removed_files), "files from cache."))
} else {
message("Cache directory is already empty.")
}
}
# Ensure digest package is available
if (!requireNamespace("digest", quietly = TRUE)) {
# This is a server-side script, so direct installation might be okay
# but ideally should be in requirements or Dockerfile.
# For now, just message that it's needed.
message("Package 'digest' is not installed. Cache key generation might not be robust. Please install it.")
}