Skip to content

Commit

Permalink
PythonRespSerializer: better chunking
Browse files Browse the repository at this point in the history
Previous implementation made no difference between memoryviews, that must
be sent separately to avoid copying, and just reaching buffer_cutoff.
That resulted in some single values being sent separately for no reason,
so a chunk could be just a single encoded integer, even though the
buffer_cutoff is pretty large.
  • Loading branch information
VadimPushtaev committed Dec 17, 2023
1 parent 715bc06 commit 8b431e9
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 30 deletions.
39 changes: 17 additions & 22 deletions redis/connection.py
Expand Up @@ -92,34 +92,29 @@ def pack(self, *args):

buff = SYM_EMPTY.join((SYM_STAR, str(len(args)).encode(), SYM_CRLF))

def get_chunks(_args):
for _arg in map(self.encode, _args):
_arg_length = len(_arg)
yield SYM_EMPTY.join((SYM_DOLLAR, str(_arg_length).encode(), SYM_CRLF))
if isinstance(_arg, memoryview):
yield _arg # we yield it independently to avoid copying memoryview
yield SYM_CRLF
else:
yield SYM_EMPTY.join((_arg, SYM_CRLF))

buffer_cutoff = self._buffer_cutoff
for arg in map(self.encode, args):
for arg in get_chunks(args):
# to avoid large string mallocs, chunk the command into the
# output list if we're sending large values or memoryviews
arg_length = len(arg)
if (
len(buff) > buffer_cutoff
or arg_length > buffer_cutoff
or isinstance(arg, memoryview)
):
buff = SYM_EMPTY.join(
(buff, SYM_DOLLAR, str(arg_length).encode(), SYM_CRLF)
)
# output list if we're sending large values
if len(buff) + len(arg) > buffer_cutoff or isinstance(arg, memoryview):
output.append(buff)
buff = SYM_EMPTY
if isinstance(arg, memoryview):
output.append(arg)
buff = SYM_CRLF
else:
buff = SYM_EMPTY.join(
(
buff,
SYM_DOLLAR,
str(arg_length).encode(),
SYM_CRLF,
arg,
SYM_CRLF,
)
)
buff = SYM_EMPTY.join((buff, arg))
output.append(buff)

return output


Expand Down
16 changes: 8 additions & 8 deletions tests/test_connection.py
Expand Up @@ -309,9 +309,9 @@ def test_pack_buffer_cutoff_max(self):

def test_pack_buffer_cutoff_average(self):
expected = [
b"*2\r\n$3\r\nGET\r\n$1\r\n",
b"a",
b"\r\n",
b"*2\r\n$3\r\n",
b"GET\r\n",
b"$1\r\na\r\n",
]
assert b"".join(expected) == self.GET_A_ENCODED

Expand All @@ -322,11 +322,11 @@ def test_pack_buffer_cutoff_average(self):

def test_pack_buffer_cutoff_min(self):
expected = [
b"*2\r\n$3\r\n",
b"GET",
b"\r\n$1\r\n",
b"a",
b"\r\n",
b"*2\r\n",
b"$3\r\n",
b"GET\r\n",
b"$1\r\n",
b"a\r\n",
]
assert b"".join(expected) == self.GET_A_ENCODED

Expand Down

0 comments on commit 8b431e9

Please sign in to comment.