Skip to content

Commit b94201f

Browse files
committed
Use orjson to improve JSON marshalling performance
https://github.com/ijl/orjson
1 parent a2aae9b commit b94201f

File tree

3 files changed

+33
-3
lines changed

3 files changed

+33
-3
lines changed

CHANGES.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Changes for crate
55
Unreleased
66
==========
77

8+
- Use ``orjson`` to improve JSON marshalling performance. Thanks, @widmogrod.
89

910
2024/11/23 1.0.1
1011
================

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ def read(path):
5454
packages=find_namespace_packages("src"),
5555
package_dir={"": "src"},
5656
install_requires=[
57+
"orjson<4",
5758
"urllib3",
5859
"verlib2",
5960
],

src/crate/client/http.py

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
from urllib.parse import urlparse
3838
from uuid import UUID
3939

40+
import orjson
4041
import urllib3
4142
from urllib3 import connection_from_url
4243
from urllib3.connection import HTTPConnection
@@ -107,6 +108,29 @@ def default(self, o):
107108
return json.JSONEncoder.default(self, o)
108109

109110

111+
def cratedb_json_encoder(obj):
112+
"""
113+
Encoder function for orjson.
114+
115+
https://github.com/ijl/orjson#default
116+
https://github.com/ijl/orjson#opt_passthrough_datetime
117+
"""
118+
if isinstance(obj, (Decimal, UUID)):
119+
return str(obj)
120+
if isinstance(obj, datetime):
121+
if obj.tzinfo is not None:
122+
delta = obj - CrateJsonEncoder.epoch_aware
123+
else:
124+
delta = obj - CrateJsonEncoder.epoch_naive
125+
return int(
126+
delta.microseconds / 1000.0
127+
+ (delta.seconds + delta.days * 24 * 3600) * 1000.0
128+
)
129+
if isinstance(obj, date):
130+
return calendar.timegm(obj.timetuple()) * 1000
131+
return obj
132+
133+
110134
class Server:
111135
def __init__(self, server, **pool_kw):
112136
socket_options = _get_socket_opts(
@@ -180,7 +204,7 @@ def close(self):
180204

181205
def _json_from_response(response):
182206
try:
183-
return json.loads(response.data.decode("utf-8"))
207+
return orjson.loads(response.data)
184208
except ValueError as ex:
185209
raise ProgrammingError(
186210
"Invalid server response of content-type '{}':\n{}".format(
@@ -223,7 +247,7 @@ def _raise_for_status_real(response):
223247
if response.status == 503:
224248
raise ConnectionError(message)
225249
if response.headers.get("content-type", "").startswith("application/json"):
226-
data = json.loads(response.data.decode("utf-8"))
250+
data = orjson.loads(response.data)
227251
error = data.get("error", {})
228252
error_trace = data.get("error_trace", None)
229253
if "results" in data:
@@ -334,7 +358,11 @@ def _create_sql_payload(stmt, args, bulk_args):
334358
data["args"] = args
335359
if bulk_args:
336360
data["bulk_args"] = bulk_args
337-
return json.dumps(data, cls=CrateJsonEncoder)
361+
return orjson.dumps(
362+
data,
363+
default=cratedb_json_encoder,
364+
option=orjson.OPT_PASSTHROUGH_DATETIME,
365+
)
338366

339367

340368
def _get_socket_opts(

0 commit comments

Comments
 (0)