Repository: matrix-org/synapse

Issue:
Creating a handful (~3) of rooms, for example to populate a new Space in Element Web, leads to running into rate limits
With default rate limiting values, we used to be able to create at least 3 rooms in one go. The new Space screen in Element Web depends on this (https://github.com/vector-im/element-web/issues/23620) and breaks if any of those room creations are rate limited, as they are now (v1.70.0).

The following test case demonstrates this:

```python
from http import HTTPStatus

from synapse.rest.client import room
from tests.unittest import HomeserverTestCase, override_config


class BisectCase(HomeserverTestCase):
    servlets = [room.register_servlets]
    hijack_auth = True
    user_id = "@sid1:test"

    @override_config({
        "rc_message": {"per_second": 0.2, "burst_count": 10},
    })
    def test_can_create_3_rooms(self):
        """
        Should be able to create 3 rooms in a row (e.g. rooms in a space)
        without hitting rate limits.
        """

        for _ in range(3):
            channel = self.make_request(
                "POST",
                "/createRoom",
                {},
            )
            self.assertEqual(channel.code, HTTPStatus.OK, channel.json_body)
```

Bisecting, the following commit is what made the above fail because of the rate limit: 8ab16a92edd675453c78cfd9974081e374b0f998 'Persist CreateRoom events to DB in a batch (#13800)' in v1.69.0.

We should try to revert rate limiting to behave the same as before or closely enough that this basic use case is not broken.


Discussion:
Is #14307 attempting to fix this?
> Is #14307 attempting to fix this?

I don't know, but it doesn't pass
These places contribute to the rate limiting in the above test:

```
	~~~~~ Ratelimited actions:

	  File "/home/rei/work/synapse/synapse/http/server.py", line 229, in wrapped_async_request_handler
	    await h(self, request)
	  File "/home/rei/work/synapse/synapse/http/server.py", line 307, in _async_render_wrapper
	    callback_return = await self._async_render(request)
	  File "/home/rei/work/synapse/synapse/http/server.py", line 513, in _async_render
	    callback_return = await raw_callback_return
	  File "/home/rei/work/synapse/synapse/rest/client/room.py", line 161, in on_POST
	    info, _ = await self._room_creation_handler.create_room(
	  File "/home/rei/work/synapse/synapse/handlers/room.py", line 756, in create_room
	    await self.request_ratelimiter.ratelimit(requester)
	  File "/home/rei/work/synapse/synapse/api/ratelimiting.py", line 391, in ratelimit
	    tb = "".join(traceback.format_stack())
	
	~+~

	  File "/home/rei/work/synapse/synapse/handlers/message.py", line 1487, in _persist_events
	    event = await self.persist_and_notify_client_events(
	  File "/home/rei/work/synapse/synapse/handlers/message.py", line 1649, in persist_and_notify_client_events
	    await self.request_ratelimiter.ratelimit(
	  File "/home/rei/work/synapse/synapse/api/ratelimiting.py", line 391, in ratelimit
	    tb = "".join(traceback.format_stack())
	
	~+~

	  File "/home/rei/work/synapse/synapse/handlers/message.py", line 1487, in _persist_events
	    event = await self.persist_and_notify_client_events(
	  File "/home/rei/work/synapse/synapse/handlers/message.py", line 1649, in persist_and_notify_client_events
	    await self.request_ratelimiter.ratelimit(
	  File "/home/rei/work/synapse/synapse/api/ratelimiting.py", line 391, in ratelimit
	    tb = "".join(traceback.format_stack())
	
	~+~

	  File "/home/rei/work/synapse/synapse/handlers/message.py", line 1487, in _persist_events
	    event = await self.persist_and_notify_client_events(
	  File "/home/rei/work/synapse/synapse/handlers/message.py", line 1649, in persist_and_notify_client_events
	    await self.request_ratelimiter.ratelimit(
	  File "/home/rei/work/synapse/synapse/api/ratelimiting.py", line 391, in ratelimit
	    tb = "".join(traceback.format_stack())
	
	~+~

	  File "/home/rei/work/synapse/synapse/handlers/message.py", line 1487, in _persist_events
	    event = await self.persist_and_notify_client_events(
	  File "/home/rei/work/synapse/synapse/handlers/message.py", line 1649, in persist_and_notify_client_events
	    await self.request_ratelimiter.ratelimit(
	  File "/home/rei/work/synapse/synapse/api/ratelimiting.py", line 391, in ratelimit
	    tb = "".join(traceback.format_stack())
	
	~+~

	  File "/home/rei/work/synapse/synapse/http/server.py", line 229, in wrapped_async_request_handler
	    await h(self, request)
	  File "/home/rei/work/synapse/synapse/http/server.py", line 307, in _async_render_wrapper
	    callback_return = await self._async_render(request)
	  File "/home/rei/work/synapse/synapse/http/server.py", line 513, in _async_render
	    callback_return = await raw_callback_return
	  File "/home/rei/work/synapse/synapse/rest/client/room.py", line 161, in on_POST
	    info, _ = await self._room_creation_handler.create_room(
	  File "/home/rei/work/synapse/synapse/handlers/room.py", line 756, in create_room
	    await self.request_ratelimiter.ratelimit(requester)
	  File "/home/rei/work/synapse/synapse/api/ratelimiting.py", line 391, in ratelimit
	    tb = "".join(traceback.format_stack())
	
	~+~

	  File "/home/rei/work/synapse/synapse/handlers/message.py", line 1487, in _persist_events
	    event = await self.persist_and_notify_client_events(
	  File "/home/rei/work/synapse/synapse/handlers/message.py", line 1649, in persist_and_notify_client_events
	    await self.request_ratelimiter.ratelimit(
	  File "/home/rei/work/synapse/synapse/api/ratelimiting.py", line 391, in ratelimit
	    tb = "".join(traceback.format_stack())
	
	~+~

	  File "/home/rei/work/synapse/synapse/handlers/message.py", line 1487, in _persist_events
	    event = await self.persist_and_notify_client_events(
	  File "/home/rei/work/synapse/synapse/handlers/message.py", line 1649, in persist_and_notify_client_events
	    await self.request_ratelimiter.ratelimit(
	  File "/home/rei/work/synapse/synapse/api/ratelimiting.py", line 391, in ratelimit
	    tb = "".join(traceback.format_stack())
	
	~+~

	  File "/home/rei/work/synapse/synapse/handlers/message.py", line 1487, in _persist_events
	    event = await self.persist_and_notify_client_events(
	  File "/home/rei/work/synapse/synapse/handlers/message.py", line 1649, in persist_and_notify_client_events
	    await self.request_ratelimiter.ratelimit(
	  File "/home/rei/work/synapse/synapse/api/ratelimiting.py", line 391, in ratelimit
	    tb = "".join(traceback.format_stack())
	
	~+~

	  File "/home/rei/work/synapse/synapse/handlers/message.py", line 1487, in _persist_events
	    event = await self.persist_and_notify_client_events(
	  File "/home/rei/work/synapse/synapse/handlers/message.py", line 1649, in persist_and_notify_client_events
	    await self.request_ratelimiter.ratelimit(
	  File "/home/rei/work/synapse/synapse/api/ratelimiting.py", line 391, in ratelimit
	    tb = "".join(traceback.format_stack())
	
	~+~

	  File "/home/rei/work/synapse/synapse/http/server.py", line 229, in wrapped_async_request_handler
	    await h(self, request)
	  File "/home/rei/work/synapse/synapse/http/server.py", line 307, in _async_render_wrapper
	    callback_return = await self._async_render(request)
	  File "/home/rei/work/synapse/synapse/http/server.py", line 513, in _async_render
	    callback_return = await raw_callback_return
	  File "/home/rei/work/synapse/synapse/rest/client/room.py", line 161, in on_POST
	    info, _ = await self._room_creation_handler.create_room(
	  File "/home/rei/work/synapse/synapse/handlers/room.py", line 756, in create_room
	    await self.request_ratelimiter.ratelimit(requester)
	  File "/home/rei/work/synapse/synapse/api/ratelimiting.py", line 391, in ratelimit
	    tb = "".join(traceback.format_stack())
	
	~~~~~
```

One might argue that we shouldn't ratelimit the creation of events whilst creating a room at all, because then you easily end up with a partially-created room which is very janky. I'd be tempted to rate limit the room's creation but not the events that go inside (?)
This is where the rate limits used to happen in v1.68.0:

```
	~~~~~ Ratelimited actions:
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 763, in _startRunCallbacks
	    self._runCallbacks()
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 857, in _runCallbacks
	    current.result = callback(  # type: ignore[misc]
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 1750, in gotResult
	    current_context.run(_inlineCallbacks, r, gen, status)
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 1660, in _inlineCallbacks
	    result = current_context.run(gen.send, result)
	  File "/home/rei/work/synapse/synapse/http/server.py", line 228, in wrapped_async_request_handler
	    await h(self, request)
	  File "/home/rei/work/synapse/synapse/http/server.py", line 306, in _async_render_wrapper
	    callback_return = await self._async_render(request)
	  File "/home/rei/work/synapse/synapse/http/server.py", line 512, in _async_render
	    callback_return = await raw_callback_return
	  File "/home/rei/work/synapse/synapse/rest/client/room.py", line 161, in on_POST
	    info, _ = await self._room_creation_handler.create_room(
	  File "/home/rei/work/synapse/synapse/handlers/room.py", line 759, in create_room
	    await self.request_ratelimiter.ratelimit(requester)
	  File "/home/rei/work/synapse/synapse/api/ratelimiting.py", line 391, in ratelimit
	    tb = "".join(traceback.format_stack(limit=10))
	
	~+~
	  File "/home/rei/work/synapse/synapse/util/metrics.py", line 113, in measured_func
	    r = await func(self, *args, **kwargs)
	  File "/home/rei/work/synapse/synapse/handlers/message.py", line 1327, in handle_new_client_event
	    run_in_background(
	  File "/home/rei/work/synapse/synapse/logging/context.py", line 814, in run_in_background
	    res = defer.ensureDeferred(res)
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 1129, in ensureDeferred
	    return Deferred.fromCoroutine(coro)
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 1105, in fromCoroutine
	    return _cancellableInlineCallbacks(coro)
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 1815, in _cancellableInlineCallbacks
	    _inlineCallbacks(None, gen, status)
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 1660, in _inlineCallbacks
	    result = current_context.run(gen.send, result)
	  File "/home/rei/work/synapse/synapse/handlers/message.py", line 1415, in _persist_event
	    event = await self.persist_and_notify_client_event(
	  File "/home/rei/work/synapse/synapse/handlers/message.py", line 1567, in persist_and_notify_client_event
	    await self.request_ratelimiter.ratelimit(
	  File "/home/rei/work/synapse/synapse/api/ratelimiting.py", line 391, in ratelimit
	    tb = "".join(traceback.format_stack(limit=10))
	
	~+~
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 763, in _startRunCallbacks
	    self._runCallbacks()
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 857, in _runCallbacks
	    current.result = callback(  # type: ignore[misc]
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 1750, in gotResult
	    current_context.run(_inlineCallbacks, r, gen, status)
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 1660, in _inlineCallbacks
	    result = current_context.run(gen.send, result)
	  File "/home/rei/work/synapse/synapse/http/server.py", line 228, in wrapped_async_request_handler
	    await h(self, request)
	  File "/home/rei/work/synapse/synapse/http/server.py", line 306, in _async_render_wrapper
	    callback_return = await self._async_render(request)
	  File "/home/rei/work/synapse/synapse/http/server.py", line 512, in _async_render
	    callback_return = await raw_callback_return
	  File "/home/rei/work/synapse/synapse/rest/client/room.py", line 161, in on_POST
	    info, _ = await self._room_creation_handler.create_room(
	  File "/home/rei/work/synapse/synapse/handlers/room.py", line 759, in create_room
	    await self.request_ratelimiter.ratelimit(requester)
	  File "/home/rei/work/synapse/synapse/api/ratelimiting.py", line 391, in ratelimit
	    tb = "".join(traceback.format_stack(limit=10))
	
	~+~
	  File "/home/rei/work/synapse/synapse/util/metrics.py", line 113, in measured_func
	    r = await func(self, *args, **kwargs)
	  File "/home/rei/work/synapse/synapse/handlers/message.py", line 1327, in handle_new_client_event
	    run_in_background(
	  File "/home/rei/work/synapse/synapse/logging/context.py", line 814, in run_in_background
	    res = defer.ensureDeferred(res)
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 1129, in ensureDeferred
	    return Deferred.fromCoroutine(coro)
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 1105, in fromCoroutine
	    return _cancellableInlineCallbacks(coro)
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 1815, in _cancellableInlineCallbacks
	    _inlineCallbacks(None, gen, status)
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 1660, in _inlineCallbacks
	    result = current_context.run(gen.send, result)
	  File "/home/rei/work/synapse/synapse/handlers/message.py", line 1415, in _persist_event
	    event = await self.persist_and_notify_client_event(
	  File "/home/rei/work/synapse/synapse/handlers/message.py", line 1567, in persist_and_notify_client_event
	    await self.request_ratelimiter.ratelimit(
	  File "/home/rei/work/synapse/synapse/api/ratelimiting.py", line 391, in ratelimit
	    tb = "".join(traceback.format_stack(limit=10))
	
	~+~
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 763, in _startRunCallbacks
	    self._runCallbacks()
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 857, in _runCallbacks
	    current.result = callback(  # type: ignore[misc]
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 1750, in gotResult
	    current_context.run(_inlineCallbacks, r, gen, status)
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 1660, in _inlineCallbacks
	    result = current_context.run(gen.send, result)
	  File "/home/rei/work/synapse/synapse/http/server.py", line 228, in wrapped_async_request_handler
	    await h(self, request)
	  File "/home/rei/work/synapse/synapse/http/server.py", line 306, in _async_render_wrapper
	    callback_return = await self._async_render(request)
	  File "/home/rei/work/synapse/synapse/http/server.py", line 512, in _async_render
	    callback_return = await raw_callback_return
	  File "/home/rei/work/synapse/synapse/rest/client/room.py", line 161, in on_POST
	    info, _ = await self._room_creation_handler.create_room(
	  File "/home/rei/work/synapse/synapse/handlers/room.py", line 759, in create_room
	    await self.request_ratelimiter.ratelimit(requester)
	  File "/home/rei/work/synapse/synapse/api/ratelimiting.py", line 391, in ratelimit
	    tb = "".join(traceback.format_stack(limit=10))
	
	~+~
	  File "/home/rei/work/synapse/synapse/util/metrics.py", line 113, in measured_func
	    r = await func(self, *args, **kwargs)
	  File "/home/rei/work/synapse/synapse/handlers/message.py", line 1327, in handle_new_client_event
	    run_in_background(
	  File "/home/rei/work/synapse/synapse/logging/context.py", line 814, in run_in_background
	    res = defer.ensureDeferred(res)
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 1129, in ensureDeferred
	    return Deferred.fromCoroutine(coro)
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 1105, in fromCoroutine
	    return _cancellableInlineCallbacks(coro)
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 1815, in _cancellableInlineCallbacks
	    _inlineCallbacks(None, gen, status)
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 1660, in _inlineCallbacks
	    result = current_context.run(gen.send, result)
	  File "/home/rei/work/synapse/synapse/handlers/message.py", line 1415, in _persist_event
	    event = await self.persist_and_notify_client_event(
	  File "/home/rei/work/synapse/synapse/handlers/message.py", line 1567, in persist_and_notify_client_event
	    await self.request_ratelimiter.ratelimit(
	  File "/home/rei/work/synapse/synapse/api/ratelimiting.py", line 391, in ratelimit
	    tb = "".join(traceback.format_stack(limit=10))
	
	~+~
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 763, in _startRunCallbacks
	    self._runCallbacks()
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 857, in _runCallbacks
	    current.result = callback(  # type: ignore[misc]
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 1750, in gotResult
	    current_context.run(_inlineCallbacks, r, gen, status)
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 1660, in _inlineCallbacks
	    result = current_context.run(gen.send, result)
	  File "/home/rei/work/synapse/synapse/http/server.py", line 228, in wrapped_async_request_handler
	    await h(self, request)
	  File "/home/rei/work/synapse/synapse/http/server.py", line 306, in _async_render_wrapper
	    callback_return = await self._async_render(request)
	  File "/home/rei/work/synapse/synapse/http/server.py", line 512, in _async_render
	    callback_return = await raw_callback_return
	  File "/home/rei/work/synapse/synapse/rest/client/room.py", line 161, in on_POST
	    info, _ = await self._room_creation_handler.create_room(
	  File "/home/rei/work/synapse/synapse/handlers/room.py", line 759, in create_room
	    await self.request_ratelimiter.ratelimit(requester)
	  File "/home/rei/work/synapse/synapse/api/ratelimiting.py", line 391, in ratelimit
	    tb = "".join(traceback.format_stack(limit=10))
	
	~+~
	  File "/home/rei/work/synapse/synapse/util/metrics.py", line 113, in measured_func
	    r = await func(self, *args, **kwargs)
	  File "/home/rei/work/synapse/synapse/handlers/message.py", line 1327, in handle_new_client_event
	    run_in_background(
	  File "/home/rei/work/synapse/synapse/logging/context.py", line 814, in run_in_background
	    res = defer.ensureDeferred(res)
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 1129, in ensureDeferred
	    return Deferred.fromCoroutine(coro)
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 1105, in fromCoroutine
	    return _cancellableInlineCallbacks(coro)
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 1815, in _cancellableInlineCallbacks
	    _inlineCallbacks(None, gen, status)
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 1660, in _inlineCallbacks
	    result = current_context.run(gen.send, result)
	  File "/home/rei/work/synapse/synapse/handlers/message.py", line 1415, in _persist_event
	    event = await self.persist_and_notify_client_event(
	  File "/home/rei/work/synapse/synapse/handlers/message.py", line 1567, in persist_and_notify_client_event
	    await self.request_ratelimiter.ratelimit(
	  File "/home/rei/work/synapse/synapse/api/ratelimiting.py", line 391, in ratelimit
	    tb = "".join(traceback.format_stack(limit=10))
	
	~+~
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 763, in _startRunCallbacks
	    self._runCallbacks()
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 857, in _runCallbacks
	    current.result = callback(  # type: ignore[misc]
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 1750, in gotResult
	    current_context.run(_inlineCallbacks, r, gen, status)
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 1660, in _inlineCallbacks
	    result = current_context.run(gen.send, result)
	  File "/home/rei/work/synapse/synapse/http/server.py", line 228, in wrapped_async_request_handler
	    await h(self, request)
	  File "/home/rei/work/synapse/synapse/http/server.py", line 306, in _async_render_wrapper
	    callback_return = await self._async_render(request)
	  File "/home/rei/work/synapse/synapse/http/server.py", line 512, in _async_render
	    callback_return = await raw_callback_return
	  File "/home/rei/work/synapse/synapse/rest/client/room.py", line 161, in on_POST
	    info, _ = await self._room_creation_handler.create_room(
	  File "/home/rei/work/synapse/synapse/handlers/room.py", line 759, in create_room
	    await self.request_ratelimiter.ratelimit(requester)
	  File "/home/rei/work/synapse/synapse/api/ratelimiting.py", line 391, in ratelimit
	    tb = "".join(traceback.format_stack(limit=10))
	
	~+~
	  File "/home/rei/work/synapse/synapse/util/metrics.py", line 113, in measured_func
	    r = await func(self, *args, **kwargs)
	  File "/home/rei/work/synapse/synapse/handlers/message.py", line 1327, in handle_new_client_event
	    run_in_background(
	  File "/home/rei/work/synapse/synapse/logging/context.py", line 814, in run_in_background
	    res = defer.ensureDeferred(res)
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 1129, in ensureDeferred
	    return Deferred.fromCoroutine(coro)
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 1105, in fromCoroutine
	    return _cancellableInlineCallbacks(coro)
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 1815, in _cancellableInlineCallbacks
	    _inlineCallbacks(None, gen, status)
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 1660, in _inlineCallbacks
	    result = current_context.run(gen.send, result)
	  File "/home/rei/work/synapse/synapse/handlers/message.py", line 1415, in _persist_event
	    event = await self.persist_and_notify_client_event(
	  File "/home/rei/work/synapse/synapse/handlers/message.py", line 1567, in persist_and_notify_client_event
	    await self.request_ratelimiter.ratelimit(
	  File "/home/rei/work/synapse/synapse/api/ratelimiting.py", line 391, in ratelimit
	    tb = "".join(traceback.format_stack(limit=10))
	
	~+~
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 763, in _startRunCallbacks
	    self._runCallbacks()
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 857, in _runCallbacks
	    current.result = callback(  # type: ignore[misc]
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 1750, in gotResult
	    current_context.run(_inlineCallbacks, r, gen, status)
	  File "/home/rei/.cache/pypoetry/virtualenvs/matrix-synapse-0QZlT1cR-py3.10/lib/python3.10/site-packages/twisted/internet/defer.py", line 1660, in _inlineCallbacks
	    result = current_context.run(gen.send, result)
	  File "/home/rei/work/synapse/synapse/http/server.py", line 228, in wrapped_async_request_handler
	    await h(self, request)
	  File "/home/rei/work/synapse/synapse/http/server.py", line 306, in _async_render_wrapper
	    callback_return = await self._async_render(request)
	  File "/home/rei/work/synapse/synapse/http/server.py", line 512, in _async_render
	    callback_return = await raw_callback_return
	  File "/home/rei/work/synapse/synapse/rest/client/room.py", line 161, in on_POST
	    info, _ = await self._room_creation_handler.create_room(
	  File "/home/rei/work/synapse/synapse/handlers/room.py", line 759, in create_room
	    await self.request_ratelimiter.ratelimit(requester)
	  File "/home/rei/work/synapse/synapse/api/ratelimiting.py", line 391, in ratelimit
	    tb = "".join(traceback.format_stack(limit=10))
	
	~~~~~
```

It appears that there was 1 event per room being rate limited, plus the room creation itself (?)
> One might argue that we shouldn't ratelimit the creation of events whilst creating a room at all [..] I'd be tempted to rate limit the room's creation but not the events that go inside

I think this likely makes sense.

Resolving patch:
diff --git a/synapse/api/ratelimiting.py b/synapse/api/ratelimiting.py
--- a/synapse/api/ratelimiting.py
+++ b/synapse/api/ratelimiting.py
@@ -343,6 +343,7 @@ async def ratelimit(
         requester: Requester,
         update: bool = True,
         is_admin_redaction: bool = False,
+        n_actions: int = 1,
     ) -> None:
         """Ratelimits requests.
 
@@ -355,6 +356,8 @@ async def ratelimit(
             is_admin_redaction: Whether this is a room admin/moderator
                 redacting an event. If so then we may apply different
                 ratelimits depending on config.
+            n_actions: Multiplier for the number of actions to apply to the
+                rate limiter at once.
 
         Raises:
             LimitExceededError if the request should be ratelimited
@@ -383,7 +386,9 @@ async def ratelimit(
         if is_admin_redaction and self.admin_redaction_ratelimiter:
             # If we have separate config for admin redactions, use a separate
             # ratelimiter as to not have user_ids clash
-            await self.admin_redaction_ratelimiter.ratelimit(requester, update=update)
+            await self.admin_redaction_ratelimiter.ratelimit(
+                requester, update=update, n_actions=n_actions
+            )
         else:
             # Override rate and burst count per-user
             await self.request_ratelimiter.ratelimit(
@@ -391,4 +396,5 @@ async def ratelimit(
                 rate_hz=messages_per_second,
                 burst_count=burst_count,
                 update=update,
+                n_actions=n_actions,
             )
diff --git a/synapse/handlers/room.py b/synapse/handlers/room.py
--- a/synapse/handlers/room.py
+++ b/synapse/handlers/room.py
@@ -559,7 +559,6 @@ async def clone_existing_room(
             invite_list=[],
             initial_state=initial_state,
             creation_content=creation_content,
-            ratelimit=False,
         )
 
         # Transfer membership events
@@ -753,6 +752,10 @@ async def create_room(
                 )
 
         if ratelimit:
+            # Rate limit once in advance, but don't rate limit the individual
+            # events in the room — room creation isn't atomic and it's very
+            # janky if half the events in the initial state don't make it because
+            # of rate limiting.
             await self.request_ratelimiter.ratelimit(requester)
 
         room_version_id = config.get(
@@ -913,7 +916,6 @@ async def create_room(
             room_alias=room_alias,
             power_level_content_override=power_level_content_override,
             creator_join_profile=creator_join_profile,
-            ratelimit=ratelimit,
         )
 
         if "name" in config:
@@ -1037,7 +1039,6 @@ async def _send_events_for_new_room(
         room_alias: Optional[RoomAlias] = None,
         power_level_content_override: Optional[JsonDict] = None,
         creator_join_profile: Optional[JsonDict] = None,
-        ratelimit: bool = True,
     ) -> Tuple[int, str, int]:
         """Sends the initial events into a new room. Sends the room creation, membership,
         and power level events into the room sequentially, then creates and batches up the
@@ -1046,6 +1047,8 @@ async def _send_events_for_new_room(
         `power_level_content_override` doesn't apply when initial state has
         power level state event content.
 
+        Rate limiting should already have been applied by this point.
+
         Returns:
             A tuple containing the stream ID, event ID and depth of the last
             event sent to the room.
@@ -1144,7 +1147,7 @@ async def send(
             creator.user,
             room_id,
             "join",
-            ratelimit=ratelimit,
+            ratelimit=False,
             content=creator_join_profile,
             new_room=True,
             prev_event_ids=[last_sent_event_id],
@@ -1269,7 +1272,10 @@ async def send(
             events_to_send.append((encryption_event, encryption_context))
 
         last_event = await self.event_creation_handler.handle_new_client_event(
-            creator, events_to_send, ignore_shadow_ban=True
+            creator,
+            events_to_send,
+            ignore_shadow_ban=True,
+            ratelimit=False,
         )
         assert last_event.internal_metadata.stream_ordering is not None
         return last_event.internal_metadata.stream_ordering, last_event.event_id, depth


Test patch:
diff --git a/tests/rest/client/test_rooms.py b/tests/rest/client/test_rooms.py
--- a/tests/rest/client/test_rooms.py
+++ b/tests/rest/client/test_rooms.py
@@ -54,6 +54,7 @@
 from tests.storage.test_stream import PaginationTestCase
 from tests.test_utils import make_awaitable
 from tests.test_utils.event_injection import create_event
+from tests.unittest import override_config
 
 PATH_PREFIX = b"/_matrix/client/api/v1"
 
@@ -871,6 +872,41 @@ async def user_may_join_room_tuple(
         self.assertEqual(channel.code, HTTPStatus.OK, channel.json_body)
         self.assertEqual(join_mock.call_count, 0)
 
+    def _create_basic_room(self) -> Tuple[int, object]:
+        """
+        Tries to create a basic room and returns the response code.
+        """
+        channel = self.make_request(
+            "POST",
+            "/createRoom",
+            {},
+        )
+        return channel.code, channel.json_body
+
+    @override_config(
+        {
+            "rc_message": {"per_second": 0.2, "burst_count": 10},
+        }
+    )
+    def test_room_creation_ratelimiting(self) -> None:
+        """
+        Regression test for #14312, where ratelimiting was made too strict.
+        Clients should be able to create 10 rooms in a row
+        without hitting rate limits, using default rate limit config.
+        (We override rate limiting config back to its default value.)
+
+        To ensure we don't make ratelimiting too generous accidentally,
+        also check that we can't create an 11th room.
+        """
+
+        for _ in range(10):
+            code, json_body = self._create_basic_room()
+            self.assertEqual(code, HTTPStatus.OK, json_body)
+
+        # The 6th room hits the rate limit.
+        code, json_body = self._create_basic_room()
+        self.assertEqual(code, HTTPStatus.TOO_MANY_REQUESTS, json_body)
+
 
 class RoomTopicTestCase(RoomBase):
     """Tests /rooms/$room_id/topic REST events."""
@@ -1390,10 +1426,22 @@ def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
     )
     def test_join_local_ratelimit(self) -> None:
         """Tests that local joins are actually rate-limited."""
-        for _ in range(3):
-            self.helper.create_room_as(self.user_id)
+        # Create 4 rooms
+        room_ids = [
+            self.helper.create_room_as(self.user_id, is_public=True) for _ in range(4)
+        ]
+
+        joiner_user_id = self.register_user("joiner", "secret")
+        # Now make a new user try to join some of them.
 
-        self.helper.create_room_as(self.user_id, expect_code=429)
+        # The user can join 3 rooms
+        for room_id in room_ids[0:3]:
+            self.helper.join(room_id, joiner_user_id)
+
+        # But the user cannot join a 4th room
+        self.helper.join(
+            room_ids[3], joiner_user_id, expect_code=HTTPStatus.TOO_MANY_REQUESTS
+        )
 
     @unittest.override_config(
         {"rc_joins": {"local": {"per_second": 0.5, "burst_count": 3}}}