Give each session a budget on the data path
Only sign-in and the account endpoints were rate limited. JMAP, blob downloads and the image and calendar proxies had no budget at all, and the proxy is one Node process that saturates a core at roughly 2,000 operations a second -- measured at 110% CPU under 150 concurrent users. One signed-in account looping requests could slow every other user on the instance. Each session now gets API_RATE_LIMIT requests a minute on those routes, 1,200 by default: twenty a second sustained, well above what a busy tab does and an order of magnitude below where one tab starts to hurt the rest. Over budget returns 429 with Retry-After. Sign-in keeps its own, separate limiter. Checked in situ: one session driven flat out was cut off after exactly 1,200 requests, and with API_RATE_LIMIT=0 throughput at 50 users is unchanged.
This commit is contained in:
@@ -298,6 +298,15 @@ export const config = {
|
||||
cookieName: env("COOKIE_NAME", "ihm_session"),
|
||||
staticDir: process.env.STATIC_DIR ?? fileURLToPath(new URL("../../web/dist", import.meta.url)),
|
||||
loginRateLimit: int("LOGIN_RATE_LIMIT", 10),
|
||||
/*
|
||||
* Requests per minute one session may make on the data path -- JMAP, blobs,
|
||||
* the image and calendar proxies. The proxy is one Node process and saturates
|
||||
* a core at roughly 2,000 operations a second, so without this a single
|
||||
* signed-in user can deny service to everyone else. 1,200 a minute is twenty
|
||||
* a second sustained: well above what a busy tab does, and an order of
|
||||
* magnitude below where one tab starts to hurt the rest. 0 disables it.
|
||||
*/
|
||||
apiRateLimit: int("API_RATE_LIMIT", 1200),
|
||||
/* See relayPushRaw(): pipe the push stream socket-to-socket instead of through fetch(). */
|
||||
rawPushRelay: process.env.RAW_PUSH_RELAY !== "0",
|
||||
/* See absoluteUpstream(): follow Stalwart's advertised origin instead of pinning to ours. */
|
||||
|
||||
Reference in New Issue
Block a user