Here I provide some fediverse config suggestions that will;
- prioritise user traffic
- deprioritise fediverse traffic
This is important, because fediverse traffic is VERY bursty, it will idle at 20req/s, and suddenly peak to 100 or 150 when a post or user needs to be resolved as someone with tons of followers will boost a post.
Fediverse traffic is resillient and will back off and retry, but during these stampedes, user request latency is affected, as (depending on how your server is setup), your server will try to process all of these requests at once, or put user requests in a queue among the hundreds of fediverse ones.
As such, there should be a way to deprioritise fediverse traffic and "put them on timeout", while prioritising user traffic, right?
Well, the simplest thing would be to tag and rate-limit federation traffic, which is fairly simple in nginx:
Lots of Nginx code
map $http_accept $has_ap_accept {
default 0;
~*application/activity\+json 1;
~*application/ld\+json 1;
~*application/json\+activitypub 1;
}
map $http_content_type $has_ap_ct {
default 0;
~*application/activity\+json 1;
~*application/ld\+json 1;
~*application/json\+activitypub 1;
}
map $request_uri $is_wellknown {
default 0;
"~^/.well-known/" 1;
}
# Fediverse has a number of ways to identify if its its own traffic
map "$has_ap_accept:$has_ap_ct:$is_wellknown" $is_ap {
# reverse, to not have to list out 3 cases
default 1;
"0:0:0" 0;
}
map $is_ap $ap_limit_key {
0 "";
1 "AP";
}
# Adjust the "rate" number as needed.
limit_req_zone $ap_limit_key zone=activitypub:10m rate=50r/s;
server {
# ...
location / {
# bit of archaic magic here, but simply;
# - "burst" (here) is the amount of requests nginx will delay by the rate limit (positioning them evenly-spaced in time)
# before nginx will start responding with 503s. In this case, its 500 (10-seconds burst worth of requests).
# - "delay" (here) is the threshold of requests per second nginx will *not* delay and space evenly,
# passing them through immidiately.
#
# Phrased otherwise: When starting from an empty rate-limit bucket,
# nginx will send 25 requests through immidiately before spacing the rest evenly in time,
# until it hits 500 outstanding requests, then it'll respond with 503s (configurable).
# 50 (rps) * 10 (sec) = 500
limit_req zone=activitypub burst=500 delay=25;
# ...
}
}
However, the rate-limit of federation requests you're able to handle before falling over is hard to find and tune, especially since user requests are still variable.
As such - while much more complicated - it would be much more stable to have a queue mechanism that would;
- be knowledgable of how many outstanding requests can be sent to the backend server, and not exceed that
- put the remaining requests in a queue
- sort that queue based on what kind of request it is (user, federation, or other)
It turns out that such a mechanism exists, haproxy's queueing mechanism, and http-request set-priority-class
.
Unfortunately, haproxy currently does not seem to have a proper certbot plugin, which means that auto-renewal would be a pain in the ass with it.
Fortunately, we still have nginx. And I just so happen to become proficient with making and manipulating variables in nginx, by abusing the map
rule.
So, currently the best solution I have on hand is;
- A way to tag and categorise traffic in nginx
- A way to queue that traffic in haproxy
And the rest of the config snippets are all about that.