Each of the Judoscale adapters work the same way—we collect metrics from web requests and job/message queues and periodically report those metrics to our Judoscale Metrics API. If you want to build your own adapter, you can follow the same pattern.
To build your adapter, you’ll need the following components:
- A web request middleware that collects metrics from incoming requests
- A mechanism for retrieving metrics for job/message queues (for autoscaling async worker services)
- A periodic reporter that sends metrics to the Judoscale Metrics API
Web request middleware
Most web frameworks have some mechanism for inserting code into the lifecycle of every web request. We’re going to call these “middlewares” for the purposes of this guide, but they may be called something else in your framework. Your middleware is responsible for calculating request queue time and storing that metric for batch reporting.
To calculate request queue time, you’ll use the X-Request-Start
header that is set by the load balancer. This header contains the time that the request was received by the load balancer. The difference between this time and the current time is the request queue time.
The X-Request-Start
header is not a standard header, and different load balances represent the timestamp in different ways. Refer to the judoscale-ruby gem source code for the known variations of this header and one way to parse it.
Once you’ve calculated a time difference in milliseconds, store this metric somewhere accessible by the periodic reporter, which we’ll discuss next.
Reporting metrics to Judoscale
Your adapter should report metrics to Judoscale in batches, rather than for every request. We recommend reporting every 10 seconds on each web and worker process. We typically use a long-running background thread or process, but you might choose to use Cron or some other mechanism.
Reporting to the Judoscale API is a simple HTTP POST request. The endpoint of the request is the concatenation of the JUDOSCALE_URL
env var and “/v3/reports”. The body of the request should be JSON in the following form:
// POST to https://adapter.judoscale.com/[TOKEN]/v3/reports
{
"container": "web.1", // identifier for the container instance; on Heroku, use the DYNO env var
"adapter": {"my-custom-adapter": "1.0.0"}, // abitrary identifier and version for your adapter
"metrics": [
[1706557668, 12, "qt"], // timestamp in unix epoch milliseconds, and the queue time value in milliseconds
[1706557679, 15, "qt"], // "qt" stands for "queue time", you'll use this for each metric
[1706557938, 1891, "qt", "default"] // you can optionally include a queue name for job/message queues
[1706557947, 10, "qt"] // metrics without a queue name are treated as web request metrics
]
}
This request will return a 204 No Content
response if successful.
Metrics for job/message queues
If you want to autoscale your “worker” services (async queues for processing jobs/messages/tasks), you’ll need to report metrics for those queues. Instead of collecting metrics for every job/message/task, we recommend querying the queues when reporting.
Judoscale autoscales worker services on “job queue time”, also known as “queue latency”, or the age of the oldest item in a queue. If your queue backend doesn’t provide a way to fetch this data, you’ll need to build it. In Sidekiq, for example, here’s how you might collect latency metrics for each queue:
queues.each do |queue_name|
queue = Sidekiq::Queue.new(queue_name)
latency_ms = (queue.latency * 1000).ceil
metrics << [Time.now.to_i, latency_ms, :qt, queue_name]
end
Let us help you!
If you’re building your own Judoscale adapter, please let us know! We’d love to help you out and make sure you’re successful. Reach out at [email protected].