Skip to content

Commit fc72990

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

File tree

4 files changed

+38
-25
lines changed

4 files changed

+38
-25
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: 33 additions & 22 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
@@ -86,25 +87,31 @@ def super_len(o):
8687
return None
8788

8889

89-
class CrateJsonEncoder(json.JSONEncoder):
90-
epoch_aware = datetime(1970, 1, 1, tzinfo=timezone.utc)
91-
epoch_naive = datetime(1970, 1, 1)
92-
93-
def default(self, o):
94-
if isinstance(o, (Decimal, UUID)):
95-
return str(o)
96-
if isinstance(o, datetime):
97-
if o.tzinfo is not None:
98-
delta = o - self.epoch_aware
99-
else:
100-
delta = o - self.epoch_naive
101-
return int(
102-
delta.microseconds / 1000.0
103-
+ (delta.seconds + delta.days * 24 * 3600) * 1000.0
104-
)
105-
if isinstance(o, date):
106-
return calendar.timegm(o.timetuple()) * 1000
107-
return json.JSONEncoder.default(self, o)
90+
epoch_aware = datetime(1970, 1, 1, tzinfo=timezone.utc)
91+
epoch_naive = datetime(1970, 1, 1)
92+
93+
94+
def cratedb_json_encoder(obj):
95+
"""
96+
Encoder function for orjson.
97+
98+
https://github.com/ijl/orjson#default
99+
https://github.com/ijl/orjson#opt_passthrough_datetime
100+
"""
101+
if isinstance(obj, (Decimal, UUID)):
102+
return str(obj)
103+
if isinstance(obj, datetime):
104+
if obj.tzinfo is not None:
105+
delta = obj - epoch_aware
106+
else:
107+
delta = obj - epoch_naive
108+
return int(
109+
delta.microseconds / 1000.0
110+
+ (delta.seconds + delta.days * 24 * 3600) * 1000.0
111+
)
112+
if isinstance(obj, date):
113+
return calendar.timegm(obj.timetuple()) * 1000
114+
return obj
108115

109116

110117
class Server:
@@ -180,7 +187,7 @@ def close(self):
180187

181188
def _json_from_response(response):
182189
try:
183-
return json.loads(response.data.decode("utf-8"))
190+
return orjson.loads(response.data)
184191
except ValueError as ex:
185192
raise ProgrammingError(
186193
"Invalid server response of content-type '{}':\n{}".format(
@@ -223,7 +230,7 @@ def _raise_for_status_real(response):
223230
if response.status == 503:
224231
raise ConnectionError(message)
225232
if response.headers.get("content-type", "").startswith("application/json"):
226-
data = json.loads(response.data.decode("utf-8"))
233+
data = orjson.loads(response.data)
227234
error = data.get("error", {})
228235
error_trace = data.get("error_trace", None)
229236
if "results" in data:
@@ -334,7 +341,11 @@ def _create_sql_payload(stmt, args, bulk_args):
334341
data["args"] = args
335342
if bulk_args:
336343
data["bulk_args"] = bulk_args
337-
return json.dumps(data, cls=CrateJsonEncoder)
344+
return orjson.dumps(
345+
data,
346+
default=cratedb_json_encoder,
347+
option=orjson.OPT_PASSTHROUGH_DATETIME,
348+
)
338349

339350

340351
def _get_socket_opts(

tests/client/test_http.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,9 @@
4949
)
5050
from crate.client.http import (
5151
Client,
52-
CrateJsonEncoder,
5352
_get_socket_opts,
5453
_remove_certs_for_non_https,
54+
cratedb_json_encoder,
5555
)
5656

5757
REQUEST = "crate.client.http.Server.request"
@@ -724,10 +724,10 @@ def test_username(self):
724724
class TestCrateJsonEncoder(TestCase):
725725
def test_naive_datetime(self):
726726
data = dt.datetime.fromisoformat("2023-06-26T09:24:00.123")
727-
result = json.dumps(data, cls=CrateJsonEncoder)
727+
result = cratedb_json_encoder(data)
728728
self.assertEqual(result, "1687771440123")
729729

730730
def test_aware_datetime(self):
731731
data = dt.datetime.fromisoformat("2023-06-26T09:24:00.123+02:00")
732-
result = json.dumps(data, cls=CrateJsonEncoder)
732+
result = cratedb_json_encoder(data)
733733
self.assertEqual(result, "1687764240123")

0 commit comments

Comments
 (0)