perf: optional orjson JSON codec behind ENABLE_ORJSON (#27583)
Swap the JSON encoder/decoder used across the backend from stdlib json to orjson when ENABLE_ORJSON is set — HTTP request bodies, JSONResponse bodies, upstream provider responses, SSE chunks, and socket.io/Redis payloads. The flag defaults to off, in which case the app uses stdlib json and engineio's codec verbatim, so default behaviour is unchanged. - json_codec exports JSONCodec (stdlib json or the orjson codec) and SOCKETIO_JSON (engineio's codec or the orjson codec); call sites import JSONCodec and stay implementation-agnostic - apply_orjson_http_json() is a no-op when the flag is off, leaving starlette's Request.json / JSONResponse.render untouched - the orjson codec falls back to the stdlib for inputs orjson rejects (non-str dict keys, ints beyond 64 bits, NaN literals) - orjson is imported only when the flag is on - FastAPI(default_response_class=...) is deliberately not used: an explicit default disables the Pydantic direct-to-bytes fast path for response_model routes
This commit is contained in:
@@ -3,11 +3,11 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import uuid
|
||||
|
||||
import pycrdt as Y
|
||||
from open_webui.env import REDIS_KEY_PREFIX
|
||||
from open_webui.utils.json_codec import JSONCodec
|
||||
from open_webui.utils.redis import get_redis_connection
|
||||
|
||||
YDOC_KEY_PREFIX = f'{REDIS_KEY_PREFIX}:ydoc:documents'
|
||||
@@ -75,14 +75,14 @@ class RedisDict:
|
||||
)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
serialized_value = json.dumps(value)
|
||||
serialized_value = JSONCodec.dumps(value)
|
||||
self.redis.hset(self.name, key, serialized_value)
|
||||
|
||||
def __getitem__(self, key):
|
||||
value = self.redis.hget(self.name, key)
|
||||
if value is None:
|
||||
raise KeyError(key)
|
||||
return json.loads(value)
|
||||
return JSONCodec.loads(value)
|
||||
|
||||
def __delitem__(self, key):
|
||||
result = self.redis.hdel(self.name, key)
|
||||
@@ -99,10 +99,10 @@ class RedisDict:
|
||||
return self.redis.hkeys(self.name)
|
||||
|
||||
def values(self):
|
||||
return [json.loads(v) for v in self.redis.hvals(self.name)]
|
||||
return [JSONCodec.loads(v) for v in self.redis.hvals(self.name)]
|
||||
|
||||
def items(self):
|
||||
return [(k, json.loads(v)) for k, v in self.redis.hgetall(self.name).items()]
|
||||
return [(k, JSONCodec.loads(v)) for k, v in self.redis.hgetall(self.name).items()]
|
||||
|
||||
def set(self, mapping: dict):
|
||||
if not mapping:
|
||||
@@ -111,7 +111,7 @@ class RedisDict:
|
||||
return
|
||||
|
||||
# Serialize values once — reused for both the fingerprint and the write.
|
||||
serialized = {k: json.dumps(v) for k, v in mapping.items()}
|
||||
serialized = {k: JSONCodec.dumps(v) for k, v in mapping.items()}
|
||||
digest = hashlib.sha256()
|
||||
for key in sorted(serialized):
|
||||
digest.update(key.encode())
|
||||
@@ -182,7 +182,7 @@ class YdocManager:
|
||||
document_id = document_id.replace(':', '_')
|
||||
if self._redis:
|
||||
redis_key = f'{self._redis_key_prefix}:{document_id}:updates'
|
||||
await self._redis.rpush(redis_key, json.dumps(list(update)))
|
||||
await self._redis.rpush(redis_key, JSONCodec.dumps(list(update)))
|
||||
list_len = await self._redis.llen(redis_key)
|
||||
if list_len >= self.COMPACTION_THRESHOLD:
|
||||
await self._compact_updates_redis(document_id)
|
||||
@@ -202,8 +202,8 @@ class YdocManager:
|
||||
mid = len(all_updates) // 2
|
||||
ydoc = Y.Doc()
|
||||
for raw in all_updates[:mid]:
|
||||
ydoc.apply_update(bytes(json.loads(raw)))
|
||||
snapshot = json.dumps(list(ydoc.get_update()))
|
||||
ydoc.apply_update(bytes(JSONCodec.loads(raw)))
|
||||
snapshot = JSONCodec.dumps(list(ydoc.get_update()))
|
||||
pipe = self._redis.pipeline()
|
||||
pipe.delete(redis_key)
|
||||
pipe.rpush(redis_key, snapshot, *all_updates[mid:])
|
||||
@@ -226,7 +226,7 @@ class YdocManager:
|
||||
if self._redis:
|
||||
redis_key = f'{self._redis_key_prefix}:{document_id}:updates'
|
||||
updates = await self._redis.lrange(redis_key, 0, -1)
|
||||
return [bytes(json.loads(update)) for update in updates]
|
||||
return [bytes(JSONCodec.loads(update)) for update in updates]
|
||||
else:
|
||||
return self._updates.get(document_id, [])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user