|
| 1 | +#' @name cache_datastore |
| 2 | +#' @title Google Datastore Cache |
| 3 | +#' @description Initiate a Google Datastore cache. |
| 4 | +#' @param project Google Cloud project |
| 5 | +#' @param cache_name datastore kind to use for storing cache entities. |
| 6 | +#' |
| 7 | +#' @examples |
| 8 | +#' \dontrun{ |
| 9 | +#' ds <- cache_datastore(project = "<project-id>", cache_name = "rcache") |
| 10 | +#' mem_memoise(runif, cache = ds) |
| 11 | +#' } |
| 12 | +#' |
| 13 | +#' @seealso \url{https://cloud.google.com/} |
| 14 | +#' @seealso \url{https://cloud.google.com/datastore/docs/concepts/overview} |
| 15 | +#' |
| 16 | +#' @export |
| 17 | + |
| 18 | +cache_datastore <- function(project, cache_name = "rcache") { |
| 19 | + |
| 20 | + if (!("googleAuthR" %in% installed.packages()[,"Package"])) { stop("googleAuthR required for datastore cache.") } |
| 21 | + |
| 22 | + options("googleAuthR.scopes.selected" = c("https://www.googleapis.com/auth/datastore", |
| 23 | + "https://www.googleapis.com/auth/userinfo.email")) |
| 24 | + |
| 25 | + googleAuthR::gar_auth() |
| 26 | + |
| 27 | + base_url <- paste0("https://datastore.googleapis.com/v1beta3/projects/", project) |
| 28 | + |
| 29 | + transaction <- googleAuthR::gar_api_generator(paste0(base_url, ":beginTransaction"), |
| 30 | + "POST", |
| 31 | + data_parse_function = function(x) x$transaction) |
| 32 | + |
| 33 | + commit_ds <- googleAuthR::gar_api_generator(paste0(base_url, ":commit"), |
| 34 | + "POST", |
| 35 | + data_parse_function = function(x) x) |
| 36 | + |
| 37 | + load_ds <- googleAuthR::gar_api_generator(paste0(base_url, ":lookup"), |
| 38 | + "POST", |
| 39 | + data_parse_function = function(resp) { |
| 40 | + # Unserialize and return |
| 41 | + if ("found" %in% names(resp)) { |
| 42 | + resp <- resp$found |
| 43 | + value <- resp$entity$properties$object$blobValue |
| 44 | + response <- unserialize(memDecompress(base64enc::base64decode(value), type = "gzip")) |
| 45 | + } else if ("missing" %in% names(resp)) { |
| 46 | + "!cache-not-found" |
| 47 | + } else { |
| 48 | + stop("Error") |
| 49 | + } |
| 50 | + }) |
| 51 | + |
| 52 | + query_ds <- googleAuthR::gar_api_generator(paste0(base_url, ":runQuery"), |
| 53 | + "POST", |
| 54 | + data_parse_function = function(resp) resp) |
| 55 | + |
| 56 | + |
| 57 | + cache_reset <- function() { |
| 58 | + query_results <- query_ds(the_body = list(gqlQuery = list(queryString = paste0("SELECT * FROM ", cache_name)))) |
| 59 | + while((query_results$batch$moreResults != "NO_MORE_RESULTS") | is.null(query_results$batch$entityResults) == FALSE) { |
| 60 | + |
| 61 | + |
| 62 | + ids <- (sapply(query_results$batch$entityResults$entity$key$path, function(x) x$name)) |
| 63 | + |
| 64 | + item_groups <- split(ids, (1:length(ids)) %/% 25) |
| 65 | + sapply(item_groups, function(idset) { |
| 66 | + mutations <- lapply(idset, function(x) { |
| 67 | + c(list("delete" = list(path = list(kind = cache_name, name = x)))) |
| 68 | + }) |
| 69 | + body <- list(mutations = mutations, transaction = transaction()) |
| 70 | + resp <- try(commit_ds(the_body = body), silent = T) |
| 71 | + message("Clearing Cache") |
| 72 | + }) |
| 73 | + query_results <- query_ds(the_body = list(gqlQuery = list(queryString = paste0("SELECT * FROM ", cache_name)))) |
| 74 | + } |
| 75 | + } |
| 76 | + |
| 77 | + |
| 78 | + cache_set <- function(key, value) { |
| 79 | + # Serialize value |
| 80 | + svalue <- base64enc::base64encode(memCompress(serialize(value, NULL, ascii=T), type = "gzip")) |
| 81 | + path_item <- list( |
| 82 | + kind = cache_name, |
| 83 | + name = key |
| 84 | + ) |
| 85 | + prop = list( |
| 86 | + object = list(blobValue = svalue, excludeFromIndexes = T) |
| 87 | + ) |
| 88 | + |
| 89 | + transaction_id <- transaction() |
| 90 | + |
| 91 | + key_obj <- c(list(key = list(path = path_item), |
| 92 | + properties = prop)) |
| 93 | + mutation = list() |
| 94 | + mutation[["upsert"]] = key_obj |
| 95 | + body <- list(mutations = mutation, |
| 96 | + transaction = transaction_id |
| 97 | + ) |
| 98 | + |
| 99 | + resp <- try(commit_ds(the_body = body), silent = T) |
| 100 | + if (class(resp) == "try-error") { |
| 101 | + warning(attr(resp, "condition")) |
| 102 | + } |
| 103 | + } |
| 104 | + |
| 105 | + cache_get <- function(key) { |
| 106 | + path_item <- list( |
| 107 | + kind = cache_name, |
| 108 | + name = key |
| 109 | + ) |
| 110 | + |
| 111 | + resp <- load_ds(the_body = list(keys = list(path = path_item))) |
| 112 | + suppressWarnings( if(resp == "!cache-not-found") { |
| 113 | + stop("Cache Not Found") |
| 114 | + }) |
| 115 | + resp |
| 116 | + } |
| 117 | + |
| 118 | + cache_has_key <- function(key) { |
| 119 | + res <- try(suppressWarnings(cache_get(key)), silent = TRUE) |
| 120 | + if (class(res) != "try-error") { |
| 121 | + message("Using Cached Version") |
| 122 | + } |
| 123 | + class(res) != "try-error" |
| 124 | + } |
| 125 | + |
| 126 | + list( |
| 127 | + reset = cache_reset, |
| 128 | + set = cache_set, |
| 129 | + get = cache_get, |
| 130 | + has_key = cache_has_key, |
| 131 | + keys = function() message("Keys can't be listed with the google datastore cache.") |
| 132 | + ) |
| 133 | +} |
| 134 | + |
| 135 | + |
| 136 | + |
0 commit comments