Lift pausing on ratelimited requests to http layer (#18595)

When a request gets ratelimited we (optionally) wait ~500ms before
returning to mitigate clients that like to tightloop on request
failures. However, this is currently implemented by pausing request
processing when we check for ratelimits, which might be deep within
request processing, and e.g. while locks are held. Instead, let's hoist
the pause to the very top of the HTTP handler.

Hopefully, this mitigates the issue where a user sending lots of events
to a single room can see their requests time out due to the combination
of the linearizer and the pausing of the request. Instead, they should
see the requests 429 after ~500ms.

The first commit is a refactor to pass the `Clock` to `AsyncResource`,
the second commit is the behavioural change.
This commit is contained in:
Erik Johnston
2025-06-25 15:32:55 +01:00
committed by GitHub
parent 0c7d9919fa
commit 0779587f9f
21 changed files with 49 additions and 34 deletions

View File

@@ -316,15 +316,16 @@ class WrapHtmlRequestHandlerTests(unittest.TestCase):
await self.callback(request)
def setUp(self) -> None:
reactor, _ = get_clock()
reactor, clock = get_clock()
self.reactor = reactor
self.clock = clock
def test_good_response(self) -> None:
async def callback(request: SynapseRequest) -> None:
request.write(b"response")
request.finish()
res = WrapHtmlRequestHandlerTests.TestResource()
res = WrapHtmlRequestHandlerTests.TestResource(self.clock)
res.callback = callback
channel = make_request(
@@ -344,7 +345,7 @@ class WrapHtmlRequestHandlerTests(unittest.TestCase):
async def callback(request: SynapseRequest, **kwargs: object) -> None:
raise RedirectException(b"/look/an/eagle", 301)
res = WrapHtmlRequestHandlerTests.TestResource()
res = WrapHtmlRequestHandlerTests.TestResource(self.clock)
res.callback = callback
channel = make_request(
@@ -366,7 +367,7 @@ class WrapHtmlRequestHandlerTests(unittest.TestCase):
e.cookies.append(b"session=yespls")
raise e
res = WrapHtmlRequestHandlerTests.TestResource()
res = WrapHtmlRequestHandlerTests.TestResource(self.clock)
res.callback = callback
channel = make_request(
@@ -387,7 +388,7 @@ class WrapHtmlRequestHandlerTests(unittest.TestCase):
request.write(b"response")
request.finish()
res = WrapHtmlRequestHandlerTests.TestResource()
res = WrapHtmlRequestHandlerTests.TestResource(self.clock)
res.callback = callback
channel = make_request(
@@ -400,7 +401,7 @@ class WrapHtmlRequestHandlerTests(unittest.TestCase):
class CancellableDirectServeJsonResource(DirectServeJsonResource):
def __init__(self, clock: Clock):
super().__init__()
super().__init__(clock)
self.clock = clock
@cancellable
@@ -417,7 +418,7 @@ class CancellableDirectServeHtmlResource(DirectServeHtmlResource):
ERROR_TEMPLATE = "{code} {msg}"
def __init__(self, clock: Clock):
super().__init__()
super().__init__(clock)
self.clock = clock
@cancellable