Representation of a user's bucket for API requests used for rate-limiting and prioritization.
A bucket can contain as many API requests as the BEAM VM and the memory can handle. However, each bucket will only process 10 API requests every 6 seconds.
The bucket provides the enqueue/1 and enqueue/2 operations providing the core functionality
of this module:
- creating the bucket if necessary,
- handling telemetry,
- and request handling.
Bucket Creation
Creating buckets is not required, but you can create and store buckets in a different manner if you need to do so.
A buckets can be created with the following two methods:
new/1to create a bucket and store the PID of the GenServer manuallyenqueue/1to create a bucket and let Tornex store the PID of the GenServer in a pre-initialized registryTornex.Scheduler.BucketRegistry
Making API Requests
API requests are made with the enqueue/1 and the enqueue/2 operations with a Tornex.Query
or Tornex.SpecQuery struct. The operations will then wait until the API call has been
scheduled and the Torn API has responded instead of ending the invocation early and using
a later await-like function.
However, for example, this can still be done with the built-in Task module (including to
handle many API requests at once):
1..10
|> Enum.map(fn n ->
Task.async(fn ->
Tornex.Scheduler.Bucket.enqueue(pid, query)
end)
end)
|> Task.await_many(timeout)If the query is a Tornex.SpecQuery that can be combined with any other Tornex.SpecQuery, including
those from other buckets, the set of queries will be merged into a Tornex.Scheduler.ExecutionUnit
that will be executed as one API call. The response of this API call will be split into the necessary
components for each Tornex.SpecQuery and these components will be returned as if the query was
performed directly. For more information, see Tornex.Scheduler.QueryRegistry and
Tornex.Scheduler.ExecutionUnit.
Bucket Timeouts
By default, the bucket will never time out when the bucket does not receive any requests.
This can be configured in milliseconds globally using the :bucket_ttl configuration key
or per-bucket using the :ttl option when starting the bucket. If the bucket does not
receive any requests for the specified amount of time, the bucket will be stopped and removed
from its supervisor. If a request is later enqueued for the removed bucket's user, a new
bucket will be created.
Summary
Functions
Returns a specification to start this module under a supervisor.
Enqueues a Tornex.Query or Tornex.SpecQuery to the queue of the Tornex.Scheduler.Bucket for the API key's user.
Retrieves the pid of the bucket by the bucket's user ID.
Attempts to start a Tornex.Scheduler.Bucket and return the pid of a bucket.
Remove a Tornex.Query or Tornex.SpecQuery from the queue of queries belonging to the bucket of the owner
of the API key used fr the query.
Starts the bucket for a user.
Types
@type state() :: %{ query_priority_queue: [Tornex.Query.t() | Tornex.SpecQuery.t()], pending_count: non_neg_integer(), dumping?: boolean(), dump_remaining: non_neg_integer(), ttl: timeout(), timeout: pos_integer() | nil }
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec enqueue(query :: Tornex.Query.t() | Tornex.SpecQuery.t(), opts :: Keyword.t()) :: term()
Enqueues a Tornex.Query or Tornex.SpecQuery to the queue of the Tornex.Scheduler.Bucket for the API key's user.
The pid of the bucket belonging to the API key's user will be retrieved with Tornex.Scheduler.Bucket.get_by_id/1,
and will be used to enqueue the query for that specific bucket.
Options
:timeout- Timeout of the GenServer call in milliseconds (default:60_000)
Retrieves the pid of the bucket by the bucket's user ID.
Using the Tornex.Scheduler.BucketRegistry that creates the relationship between the bucket and the user ID upon
bucket creation, the pid of the bucket will be returned. If a bucket is not registered for that user, :error
will be returned.
Attempts to start a Tornex.Scheduler.Bucket and return the pid of a bucket.
The Tornex.Scheduler.Supervisor will attempt to start the bucket as a child. If the bucket does not exist,
the bucket will be created and the pid of the bucket will be returned. If the bucket is already a child of
Tornex.Scheduler.Supervisor, the pid of the existing bucket will be returned.
If there is an error in retrieving the pid of the bucket or an error in creating the bucket (e.g. maximum
number of children in the supervisor reached), nil will be returned.
Options
:ttl- (integer) Time in milliseconds before the bucket will be stopped without any requests (defaults to:bucket_ttl)
Examples
iex> Tornex.Scheduler.Bucket.new(2383326)
{:nonode@host, #PID<0.105.0>}
@spec pop!(query :: Tornex.Query.t() | Tornex.SpecQuery.t(), opts :: keyword()) :: :ok
Remove a Tornex.Query or Tornex.SpecQuery from the queue of queries belonging to the bucket of the owner
of the API key used fr the query.
@spec start_link(opts :: keyword()) :: GenServer.on_start()
Starts the bucket for a user.
Options
:user_id- (any) A required unique identifier for the user the bucket belongs to:ttl- (integer) Time in milliseconds before the bucket will be stopped without any requests (defaults to:bucket_ttl)
Examples
iex> Tornex.Scheduler.Bucket.start_link(user_id: 2383326, ttl: 10_000)
{:ok, process}
iex> is_pid(process)
true