Skip to content

req_throttle() realms lost/uninitialized using library() but works with load_all() #779

@smartiing

Description

@smartiing

Hi and thank you for this amazing package.

I'm running into an issue with req_throttle() when used in a function factory inside an unpublished package. I suspect this may be due to how the throttling state is stored and initialized, possibly combined with my use of function factory/closures.

Here's a simplified version of my code without documentation:

# --- I define a function factory
#' @export
req_function_factory = function(endpoint, body, realm = c("email", "sms", "others")) {
  realms = list(
    email  = list(capacity = 6000L, fill_time_s = 1L),
    sms    = list(capacity = 9L, fill_time_s = 1L),
    others = list(capacity = 60L, fill_time_s = 1L)
  )
  
  realm = match.arg(realm)
  
  req = 
    httr2::request("https://api.example.com") |>
    httr2::req_url_path_append(endpoint) |>
    httr2::req_method("GET") |>
    httr2::req_headers("api-key" = "secret") |>
    httr2::req_throttle(
      capacity    = realms[[realm]]$capacity,
      fill_time_s = realms[[realm]]$fill_time_s,
      realm       = realm
    )
  
  function(body = NULL) {
    req |>
      httr2::req_body_json(body, auto_unbox = FALSE, null = "list")
  }
}

# --- Then I create the functions for the endpoints that I need to use
#' @export
req_get_list = req_function_factory("/list", NULL, "email")

#' @export
req_get_sender = req_function_factory("/sender", NULL, "sms")

#...

I have been using this code for months and it works fine. I recently however added the call to httr2::req_throttle() and it didn't work as expected. When I install and load the package normally with library(mypackage), creating a request with one of the function works but performing the request doesn't.

Error in the$throttle[[req$policies$throttle_realm]]$take_token() : 
  attempt to apply non-function

But when I use devtools::load_all(), the same call works without issue.

I stepped into httr2:::throttle_delay() and noticed that the$throttle is empty after using library(). When inspecting the content of httr2:::the$throttle, it is indeed different:

> rlang::env_names(httr2:::the)
# [1] "pool_pollers"   "token_cache"    "last_response"  "breaker"        "last_request"   "throttle"       "cache_throttle" "last_interrupt"
> rlang::env_names(httr2:::the$throttle)
# character(0)
> library(mypackage)
> rlang::env_names(httr2:::the$throttle)
# character(0)
> devtools::load_all()
# ℹ Loading mypackage
> rlang::env_names(httr2:::the$throttle)
# [1] "all_contacts" "all_others"   "rest_smtp"   
> httr2:::the$throttle$all_contacts
# <TokenBucket>
#   Public:
#     capacity: 60
#     clone: function (deep = FALSE) 
#     fill_rate: 60
#     initialize: function (capacity, fill_time_s) 
#     last_fill: 1752663218
#     refill: function () 
#     return_token: function () 
#     take_token: function () 
#     token_wait_time: function () 
#     tokens: 60

I probably simply don't understand why there is a difference in the way httr2 is setting values in httr2:::the$throttle when using library() vs load_all(). I'm not sure whether this is expected behavior or if there's a better pattern I should be following that I don't know.

Thank you in advance for your help.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions