Metrics
The limiter publishes metrics with System.Diagnostics.Metrics. The meter is Nall.RateLimiting.Adaptive, also in the constant AdaptiveRateLimiterMetrics.MeterName.
If the container has an IMeterFactory, the DI registration uses it. The generic host and ASP.NET Core add one. Without DI, pass an IMeterFactory to the constructor, or the limiter uses one static meter.
Collect with OpenTelemetry
Section titled “Collect with OpenTelemetry”builder .Services.AddOpenTelemetry() .WithMetrics(metrics => metrics .AddMeter("Nall.RateLimiting.Adaptive") .AddView( "ratelimit.adaptive.wait", new ExplicitBucketHistogramConfiguration { Boundaries = [0, 0.1, 0.5, 1, 2, 5, 10, 30, 60, 100], } ) .AddConsoleExporter() );The view is optional. The default histogram boundaries of the OpenTelemetry SDK go from 0 to 10,000, which fits milliseconds. Waits are in seconds and can be up to MaxWait, so set boundaries in seconds. Replace the console exporter with the exporter of your backend.
Instruments
Section titled “Instruments”Every measurement has the tag ratelimit.limiter with the name of the limiter.
| Instrument | Type | Unit | Tags | Meaning |
|---|---|---|---|---|
ratelimit.adaptive.units |
Counter long |
{unit} |
ratelimit.category |
Units sent. Recorded when a request gets its units, also when the limiter is disabled. |
ratelimit.adaptive.throttled |
Counter long |
{response} |
http.response.status_code, ratelimit.retry_after, ratelimit.category |
Throttled responses. A $batch adds one for each throttled inner response. |
ratelimit.adaptive.rejected |
Counter long |
{request} |
ratelimit.reason |
Requests that the limiter did not send. |
ratelimit.adaptive.wait |
Histogram double |
s |
none | The time that a request waited for its units. A request that did not wait records 0. |
ratelimit.adaptive.rate |
Observable gauge double |
{unit}/min |
none | The current rate of this process. |
ratelimit.adaptive.queue |
Observable gauge int |
{request} |
none | Requests that wait for units. |
ratelimit.adaptive.instances |
Observable gauge int |
{process} |
none | The processes that share the budget. |
The gauges report only limiters that are enabled. The wait histogram records only for enabled limiters too. A request of 0 units records nothing on units or wait.
Tag values
Section titled “Tag values”| Tag | Values |
|---|---|
ratelimit.limiter |
The name of the limiter. |
ratelimit.category |
RequestCost.Category, or default if it is null. |
http.response.status_code |
The status code of the throttled response, for example 429 or 503. |
ratelimit.retry_after |
none, le_5s (5 seconds or less), le_30s (30 seconds or less) or gt_30s. |
ratelimit.reason |
queue_full or timeout. |
The constants of the instrument and tag names are on AdaptiveRateLimiterMetrics.
What to watch
Section titled “What to watch”| Question | Query |
|---|---|
| How much of the budget do we use? | Rate of ratelimit.adaptive.units, in units per minute, summed over all processes. Compare it with the limit of the API. |
| Does the limiter find the limit? | ratelimit.adaptive.rate summed over all processes, next to the rate of units. |
| Do we get throttled? | Rate of ratelimit.adaptive.throttled by http.response.status_code and ratelimit.retry_after. A few throttles are normal: the limiter finds the limit by them. |
| Is the client too slow for its work? | p95 of ratelimit.adaptive.wait, ratelimit.adaptive.queue, and the rate of ratelimit.adaptive.rejected by ratelimit.reason. |
| Is the share right? | ratelimit.adaptive.instances on each process. All processes of one budget must show the same number. |
Dashboard panels
Section titled “Dashboard panels”A useful dashboard has one row per limiter:
- Units per minute (rate of
units, stacked byratelimit.category), with a line for the sum ofrate. - Throttles per minute (rate of
throttled, byratelimit.retry_after). - Wait (p50 and p95 of
wait) and queue (max ofqueue). - Rejections per minute (rate of
rejected, byratelimit.reason). - Instances (last value of
instances).
The simulator shows the same values: units used, sum of rates, 429s, queue, p95 wait and rejections.