perf: use orjson for faster request processing

This commit is contained in:
Sagar Vora 2025-06-21 21:44:11 +05:30
parent 1a1dc0a62c
commit b857a4099a
4 changed files with 27 additions and 15 deletions

View file

@ -34,6 +34,7 @@ from typing import (
)
import click
import orjson
from werkzeug.datastructures import Headers
import frappe
@ -1265,7 +1266,7 @@ def get_installed_apps(*, _ensure_on_bench: bool = False) -> list[str]:
if not db:
connect()
installed = json.loads(db.get_global("installed_apps") or "[]")
installed = orjson.loads(db.get_global("installed_apps") or "[]")
if _ensure_on_bench:
all_apps = cache.get_value("all_apps", get_all_apps)

View file

@ -5,6 +5,7 @@ import functools
import logging
import os
import orjson
from werkzeug.exceptions import HTTPException, NotFound
from werkzeug.middleware.profiler import ProfilerMiddleware
from werkzeug.middleware.proxy_fix import ProxyFix
@ -297,11 +298,9 @@ def set_cors_headers(response):
def make_form_dict(request: Request):
import json
request_data = request.get_data(as_text=True)
if request_data and request.is_json:
args = json.loads(request_data)
args = orjson.loads(request_data)
else:
args = {}
args.update(request.args or {})

View file

@ -2,18 +2,19 @@
# License: MIT. See LICENSE
import datetime
import decimal
import json
import functools
import mimetypes
import os
import sys
import uuid
from collections.abc import Iterable
from decimal import Decimal
from pathlib import Path
from re import Match
from typing import TYPE_CHECKING
from urllib.parse import quote
from uuid import UUID
import orjson
import werkzeug.utils
from werkzeug.exceptions import Forbidden, NotFound
from werkzeug.local import LocalProxy
@ -32,6 +33,7 @@ if TYPE_CHECKING:
from frappe.core.doctype.file.file import File
DateOrTimeTypes = datetime.date | datetime.datetime | datetime.time
timedelta = datetime.timedelta
def report_error(status_code):
@ -148,7 +150,7 @@ def as_json():
del frappe.local.response["http_status_code"]
response.mimetype = "application/json"
response.data = json.dumps(frappe.local.response, default=json_handler, separators=(",", ":"))
response.data = dump_response(frappe.local.response)
return response
@ -191,13 +193,15 @@ def _make_logs_v1():
if frappe.error_log and is_traceback_allowed():
if source := guess_exception_source(frappe.local.error_log and frappe.local.error_log[0]["exc"]):
response["_exc_source"] = source
response["exc"] = json.dumps([frappe.utils.cstr(d["exc"]) for d in frappe.local.error_log])
response["exc"] = orjson.dumps([frappe.utils.cstr(d["exc"]) for d in frappe.local.error_log]).decode()
if frappe.local.message_log:
response["_server_messages"] = json.dumps([json.dumps(d) for d in frappe.local.message_log])
response["_server_messages"] = orjson.dumps(
[orjson.dumps(d).decode() for d in frappe.local.message_log]
).decode()
if frappe.debug_log:
response["_debug_messages"] = json.dumps(frappe.local.debug_log)
response["_debug_messages"] = orjson.dumps(frappe.local.debug_log).decode()
if frappe.flags.error_message:
response["_error_message"] = frappe.flags.error_message
@ -219,7 +223,7 @@ def json_handler(obj):
if isinstance(obj, DateOrTimeTypes):
return str(obj)
elif isinstance(obj, datetime.timedelta):
elif isinstance(obj, timedelta):
return format_timedelta(obj)
elif isinstance(obj, LocalProxy):
@ -231,7 +235,7 @@ def json_handler(obj):
elif isinstance(obj, Iterable):
return list(obj)
elif isinstance(obj, decimal.Decimal):
elif isinstance(obj, Decimal):
return float(obj)
elif isinstance(obj, Match):
@ -243,16 +247,23 @@ def json_handler(obj):
elif callable(obj):
return repr(obj)
elif isinstance(obj, uuid.UUID):
elif isinstance(obj, Path):
return str(obj)
elif isinstance(obj, Path):
# orjson does this already
# but json_handler needs to be compatible with built-in json module also
elif isinstance(obj, UUID):
return str(obj)
else:
raise TypeError(f"""Object of type {type(obj)} with value of {obj!r} is not JSON serializable""")
dump_response = functools.partial(
orjson.dumps, default=json_handler, option=orjson.OPT_PASSTHROUGH_DATETIME | orjson.OPT_NON_STR_KEYS
)
def as_page():
"""print web page"""
from frappe.website.serve import get_response

View file

@ -47,6 +47,7 @@ dependencies = [
"num2words~=0.5.14",
"oauthlib~=3.2.2",
"openpyxl~=3.1.5",
"orjson~=3.10.18",
"passlib~=1.7.4",
"pdfkit~=1.0.0",
"phonenumbers~=9.0.7",