mirror of
https://git.anonymousland.org/anonymousland/synapse.git
synced 2025-05-03 09:24:50 -04:00
Encode JSON responses on a thread in C, mk2 (#10905)
Currently we use `JsonEncoder.iterencode` to write JSON responses, which ensures that we don't block the main reactor thread when encoding huge objects. The downside to this is that `iterencode` falls back to using a pure Python encoder that is *much* less efficient and can easily burn a lot of CPU for huge responses. To fix this, while still ensuring we don't block the reactor loop, we encode the JSON on a threadpool using the standard `JsonEncoder.encode` functions, which is backed by a C library. Doing so, however, requires `respond_with_json` to have access to the reactor, which it previously didn't. There are two ways of doing this: 1. threading through the reactor object, which is a bit fiddly as e.g. `DirectServeJsonResource` doesn't currently take a reactor, but is exposed to modules and so is a PITA to change; or 2. expose the reactor in `SynapseRequest`, which requires updating a bunch of servlet types. I went with the latter as that is just a mechanical change, and I think makes sense as a request already has a reactor associated with it (via its http channel).
This commit is contained in:
parent
d37841787a
commit
707d5e4e48
4 changed files with 76 additions and 18 deletions
|
@ -21,13 +21,28 @@ from typing import (
|
|||
Iterable,
|
||||
Iterator,
|
||||
Mapping,
|
||||
Sequence,
|
||||
Set,
|
||||
Sized,
|
||||
Tuple,
|
||||
TypeVar,
|
||||
)
|
||||
|
||||
from typing_extensions import Protocol
|
||||
|
||||
T = TypeVar("T")
|
||||
S = TypeVar("S", bound="_SelfSlice")
|
||||
|
||||
|
||||
class _SelfSlice(Sized, Protocol):
|
||||
"""A helper protocol that matches types where taking a slice results in the
|
||||
same type being returned.
|
||||
|
||||
This is more specific than `Sequence`, which allows another `Sequence` to be
|
||||
returned.
|
||||
"""
|
||||
|
||||
def __getitem__(self: S, i: slice) -> S:
|
||||
...
|
||||
|
||||
|
||||
def batch_iter(iterable: Iterable[T], size: int) -> Iterator[Tuple[T, ...]]:
|
||||
|
@ -46,7 +61,7 @@ def batch_iter(iterable: Iterable[T], size: int) -> Iterator[Tuple[T, ...]]:
|
|||
return iter(lambda: tuple(islice(sourceiter, size)), ())
|
||||
|
||||
|
||||
def chunk_seq(iseq: Sequence[T], maxlen: int) -> Iterable[Sequence[T]]:
|
||||
def chunk_seq(iseq: S, maxlen: int) -> Iterator[S]:
|
||||
"""Split the given sequence into chunks of the given size
|
||||
|
||||
The last chunk may be shorter than the given size.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue