Skip to content

Commit dd0be28

Browse files
authored
[DE-716] Compression of requests and responses (#328)
* Compression of requests works * Accepting encoded requests * Refactoring request compression as a plugin * Setting default compression values * Updating docs * Adding content_encoding * Adding content_encoding * Small fix * Skip interface from coverage
1 parent ead2e66 commit dd0be28

File tree

8 files changed

+266
-17
lines changed

8 files changed

+266
-17
lines changed

arango/client.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,12 @@
1313
)
1414
from arango.database import StandardDatabase
1515
from arango.exceptions import ServerConnectionError
16-
from arango.http import DEFAULT_REQUEST_TIMEOUT, DefaultHTTPClient, HTTPClient
16+
from arango.http import (
17+
DEFAULT_REQUEST_TIMEOUT,
18+
DefaultHTTPClient,
19+
HTTPClient,
20+
RequestCompression,
21+
)
1722
from arango.resolver import (
1823
FallbackHostResolver,
1924
HostResolver,
@@ -33,7 +38,7 @@ def default_serializer(x: Any) -> str:
3338
:return: The object serialized as a JSON string
3439
:rtype: str
3540
"""
36-
return dumps(x)
41+
return dumps(x, separators=(",", ":"))
3742

3843

3944
def default_deserializer(x: str) -> Any:
@@ -85,6 +90,12 @@ class ArangoClient:
8590
None: No timeout.
8691
int: Timeout value in seconds.
8792
:type request_timeout: int | float
93+
:param request_compression: Will compress requests to the server according to
94+
the given algorithm. No compression happens by default.
95+
:type request_compression: arango.http.RequestCompression | None
96+
:param response_compression: Tells the server what compression algorithm is
97+
acceptable for the response. No compression happens by default.
98+
:type response_compression: str | None
8899
"""
89100

90101
def __init__(
@@ -97,6 +108,8 @@ def __init__(
97108
deserializer: Callable[[str], Any] = default_deserializer,
98109
verify_override: Union[bool, str, None] = None,
99110
request_timeout: Union[int, float, None] = DEFAULT_REQUEST_TIMEOUT,
111+
request_compression: Optional[RequestCompression] = None,
112+
response_compression: Optional[str] = None,
100113
) -> None:
101114
if isinstance(hosts, str):
102115
self._hosts = [host.strip("/") for host in hosts.split(",")]
@@ -133,6 +146,9 @@ def __init__(
133146
for session in self._sessions:
134147
session.verify = verify_override
135148

149+
self._request_compression = request_compression
150+
self._response_compression = response_compression
151+
136152
def __repr__(self) -> str:
137153
return f"<ArangoClient {','.join(self._hosts)}>"
138154

@@ -231,6 +247,8 @@ def db(
231247
serializer=self._serializer,
232248
deserializer=self._deserializer,
233249
superuser_token=superuser_token,
250+
request_compression=self._request_compression,
251+
response_compression=self._response_compression,
234252
)
235253
elif user_token is not None:
236254
connection = JwtConnection(
@@ -242,6 +260,8 @@ def db(
242260
serializer=self._serializer,
243261
deserializer=self._deserializer,
244262
user_token=user_token,
263+
request_compression=self._request_compression,
264+
response_compression=self._response_compression,
245265
)
246266
elif auth_method.lower() == "basic":
247267
connection = BasicConnection(
@@ -254,6 +274,8 @@ def db(
254274
http_client=self._http,
255275
serializer=self._serializer,
256276
deserializer=self._deserializer,
277+
request_compression=self._request_compression,
278+
response_compression=self._response_compression,
257279
)
258280
elif auth_method.lower() == "jwt":
259281
connection = JwtConnection(
@@ -266,6 +288,8 @@ def db(
266288
http_client=self._http,
267289
serializer=self._serializer,
268290
deserializer=self._deserializer,
291+
request_compression=self._request_compression,
292+
response_compression=self._response_compression,
269293
)
270294
else:
271295
raise ValueError(f"invalid auth_method: {auth_method}")

arango/collection.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1813,7 +1813,7 @@ def insert_many(
18131813
index caches if document insertions affect the edge index or
18141814
cache-enabled persistent indexes.
18151815
:type refill_index_caches: bool | None
1816-
param version_attribute: support for simple external versioning to
1816+
:param version_attribute: support for simple external versioning to
18171817
document operations.
18181818
:type version_attribute: str
18191819
:return: List of document metadata (e.g. document keys, revisions) and
@@ -1939,7 +1939,7 @@ def update_many(
19391939
as opposed to returning the error as an object in the result list.
19401940
Defaults to False.
19411941
:type raise_on_document_error: bool
1942-
param version_attribute: support for simple external versioning to
1942+
:param version_attribute: support for simple external versioning to
19431943
document operations.
19441944
:type version_attribute: str
19451945
:return: List of document metadata (e.g. document keys, revisions) and
@@ -2138,7 +2138,7 @@ def replace_many(
21382138
index caches if document operations affect the edge index or
21392139
cache-enabled persistent indexes.
21402140
:type refill_index_caches: bool | None
2141-
param version_attribute: support for simple external versioning to
2141+
:param version_attribute: support for simple external versioning to
21422142
document operations.
21432143
:type version_attribute: str
21442144
:return: List of document metadata (e.g. document keys, revisions) and
@@ -2670,7 +2670,7 @@ def insert(
26702670
index caches if document insertions affect the edge index or
26712671
cache-enabled persistent indexes.
26722672
:type refill_index_caches: bool | None
2673-
param version_attribute: support for simple external versioning to
2673+
:param version_attribute: support for simple external versioning to
26742674
document operations.
26752675
:type version_attribute: str
26762676
:return: Document metadata (e.g. document key, revision) or True if
@@ -2765,7 +2765,7 @@ def update(
27652765
index caches if document insertions affect the edge index or
27662766
cache-enabled persistent indexes.
27672767
:type refill_index_caches: bool | None
2768-
param version_attribute: support for simple external versioning
2768+
:param version_attribute: support for simple external versioning
27692769
to document operations.
27702770
:type version_attribute: str
27712771
:return: Document metadata (e.g. document key, revision) or True if
@@ -2850,7 +2850,7 @@ def replace(
28502850
index caches if document insertions affect the edge index or
28512851
cache-enabled persistent indexes.
28522852
:type refill_index_caches: bool | None
2853-
param version_attribute: support for simple external versioning to
2853+
:param version_attribute: support for simple external versioning to
28542854
document operations.
28552855
:type version_attribute: str
28562856
:return: Document metadata (e.g. document key, revision) or True if

arango/connection.py

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
JWTRefreshError,
2424
ServerConnectionError,
2525
)
26-
from arango.http import HTTPClient
26+
from arango.http import HTTPClient, RequestCompression
2727
from arango.request import Request
2828
from arango.resolver import HostResolver
2929
from arango.response import Response
@@ -44,6 +44,8 @@ def __init__(
4444
http_client: HTTPClient,
4545
serializer: Callable[..., str],
4646
deserializer: Callable[[str], Any],
47+
request_compression: Optional[RequestCompression] = None,
48+
response_compression: Optional[str] = None,
4749
) -> None:
4850
self._url_prefixes = [f"{host}/_db/{db_name}" for host in hosts]
4951
self._host_resolver = host_resolver
@@ -53,6 +55,8 @@ def __init__(
5355
self._serializer = serializer
5456
self._deserializer = deserializer
5557
self._username: Optional[str] = None
58+
self._request_compression = request_compression
59+
self._response_compression = response_compression
5660

5761
@property
5862
def db_name(self) -> str:
@@ -133,14 +137,27 @@ def process_request(
133137
"""
134138
tries = 0
135139
indexes_to_filter: Set[int] = set()
140+
141+
data = self.normalize_data(request.data)
142+
if (
143+
self._request_compression is not None
144+
and isinstance(data, str)
145+
and self._request_compression.needs_compression(data)
146+
):
147+
request.headers["content-encoding"] = self._request_compression.encoding()
148+
data = self._request_compression.compress(data)
149+
150+
if self._response_compression is not None:
151+
request.headers["accept-encoding"] = self._response_compression
152+
136153
while tries < self._host_resolver.max_tries:
137154
try:
138155
resp = self._http.send_request(
139156
session=self._sessions[host_index],
140157
method=request.method,
141158
url=self._url_prefixes[host_index] + request.endpoint,
142159
params=request.params,
143-
data=self.normalize_data(request.data),
160+
data=data,
144161
headers=request.headers,
145162
auth=auth,
146163
)
@@ -243,6 +260,10 @@ class BasicConnection(BaseConnection):
243260
:type password: str
244261
:param http_client: User-defined HTTP client.
245262
:type http_client: arango.http.HTTPClient
263+
:param: request_compression: The request compression algorithm.
264+
:type request_compression: arango.http.RequestCompression | None
265+
:param: response_compression: The response compression algorithm.
266+
:type response_compression: str | None
246267
"""
247268

248269
def __init__(
@@ -256,6 +277,8 @@ def __init__(
256277
http_client: HTTPClient,
257278
serializer: Callable[..., str],
258279
deserializer: Callable[[str], Any],
280+
request_compression: Optional[RequestCompression] = None,
281+
response_compression: Optional[str] = None,
259282
) -> None:
260283
super().__init__(
261284
hosts,
@@ -265,6 +288,8 @@ def __init__(
265288
http_client,
266289
serializer,
267290
deserializer,
291+
request_compression,
292+
response_compression,
268293
)
269294
self._username = username
270295
self._auth = (username, password)
@@ -298,6 +323,10 @@ class JwtConnection(BaseConnection):
298323
:type password: str
299324
:param http_client: User-defined HTTP client.
300325
:type http_client: arango.http.HTTPClient
326+
:param request_compression: The request compression algorithm.
327+
:type request_compression: arango.http.RequestCompression | None
328+
:param response_compression: The response compression algorithm.
329+
:type response_compression: str | None
301330
"""
302331

303332
def __init__(
@@ -312,6 +341,8 @@ def __init__(
312341
username: Optional[str] = None,
313342
password: Optional[str] = None,
314343
user_token: Optional[str] = None,
344+
request_compression: Optional[RequestCompression] = None,
345+
response_compression: Optional[str] = None,
315346
) -> None:
316347
super().__init__(
317348
hosts,
@@ -321,6 +352,8 @@ def __init__(
321352
http_client,
322353
serializer,
323354
deserializer,
355+
request_compression,
356+
response_compression,
324357
)
325358
self._username = username
326359
self._password = password
@@ -439,6 +472,10 @@ class JwtSuperuserConnection(BaseConnection):
439472
:type http_client: arango.http.HTTPClient
440473
:param superuser_token: User generated token for superuser access.
441474
:type superuser_token: str
475+
:param request_compression: The request compression algorithm.
476+
:type request_compression: arango.http.RequestCompression | None
477+
:param response_compression: The response compression algorithm.
478+
:type response_compression: str | None
442479
"""
443480

444481
def __init__(
@@ -451,6 +488,8 @@ def __init__(
451488
serializer: Callable[..., str],
452489
deserializer: Callable[[str], Any],
453490
superuser_token: str,
491+
request_compression: Optional[RequestCompression] = None,
492+
response_compression: Optional[str] = None,
454493
) -> None:
455494
super().__init__(
456495
hosts,
@@ -460,6 +499,8 @@ def __init__(
460499
http_client,
461500
serializer,
462501
deserializer,
502+
request_compression,
503+
response_compression,
463504
)
464505
self._auth_header = f"bearer {superuser_token}"
465506

0 commit comments

Comments
 (0)