Skip to content

add orjson - #6116

Draft
benedikt-bartscher wants to merge 70 commits into
reflex-dev:mainfrom
benedikt-bartscher:try-orjson
Draft

benedikt-bartscher wants to merge 70 commits into
reflex-dev:mainfrom
benedikt-bartscher:try-orjson

Conversation

@benedikt-bartscher

@benedikt-bartscher benedikt-bartscher commented Feb 6, 2026 •

Copy link
Copy Markdown
Contributor

State updates over the websocket and generated frontend files are serialized with orjson when the new reflex[orjson] extra is installed; without it Reflex falls back to the stdlib json module. Output is byte-compatible between the two backends, and inbound socket integers beyond 64 bits still reach event handlers exactly.

Benchmarks

Event wire path — decode 3 incoming events, process them, serialize 3 deltas — measured with each revision's own socket codec (_sio_dumps / _sio_loads). Python 3.14.7, 500 rounds, 3 interleaved repetitions; medians, ±1–2% run to run.

small delta (~2 KB) large delta (~200 KB)
this branch + orjson 217.0 µs (−5.0%) 1234.3 µs (−28.0%, 1.39×)
this branch, no orjson 234.8 µs (+2.8%) 1811.5 µs (+5.7%)
main 228.4 µs 1714.1 µs

Outgoing encode in isolation (95.7 KB payload):

encode
this branch + orjson 104.4 µs (2.44×)
this branch, no orjson 284.0 µs (+11.3%)
main 255.2 µs

Without the extra, the fallback adds two scans of the serialized string on top of the stdlib dump — the sentinel-collision check and the isascii() UTF-8 guard — about +29 µs on 95.7 KB.

@codspeed

codspeed Bot commented Feb 6, 2026 •

Copy link
Copy Markdown

Merging this PR will not alter performance

⚠️ 15 benchmarks spent significant time in system calls

System calls cannot be consistently instrumented, so they are not included in the measure, which understates the real cost. Please switch to the Walltime instrument to accurately measure system calls.

Measurement and system calls

✅ 148 untouched benchmarks
🆕 2 new benchmarks

Performance Changes

Benchmark BASE HEAD Efficiency
🆕 test_process_event_wire[large_delta] N/A 38.8 ms N/A
🆕 test_process_event_wire[small_delta] N/A 3.1 ms N/A

Comparing benedikt-bartscher:try-orjson (a0154b5) with main (4be8b4d)

Open in CodSpeed

@benedikt-bartscher
benedikt-bartscher marked this pull request as ready for review February 7, 2026 13:38
@greptile-apps

greptile-apps Bot commented Feb 7, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 4/5

[Medium impact] The PR changes JSON handling across generated app files and live Reflex updates.

Fix the shared-base serializer bypass before merging; installing the extra can change outgoing values.

Findings

  1. P1 Shared serializers are skipped ▶
  2. P2 Benchmark measures the wrong decoder ▶

Summary

Adds optional orjson encoding for state updates and generated frontend files, with stdlib fallbacks.

  • Reflex uses optional orjson to encode socket updates and generated files.
  • Reflex keeps incoming socket integers exact and scopes its codec to its own server.
  • Package override updates leave non-object settings untouched.

Previous threads were unnumbered, so no numbered classifications are recorded. benedikt-bartscher disputed the Plotly auto-engine findings because its dump and parse engines agree; those findings are set aside. The exception-inheritance objection was also correctly disputed by benedikt-bartscher.

Reviews (44) · Last reviewed commit: "Merge remote-tracking branch 'upstream/m..." · Reviewed by Greptile

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment thread reflex/app.py Resolved
@adhami3310

Copy link
Copy Markdown
Member

the issue with orjson is that we lose NaN handling which is relevant for numpy, which is why we were using JSON5 as well

@benedikt-bartscher

benedikt-bartscher commented Feb 12, 2026 •

Copy link
Copy Markdown
Contributor Author

Yeah, that's why i did not update it in all places

benedikt-bartscher and others added 3 commits February 24, 2026 21:41
…llback

Happy path uses native JSON.parse; the catch rewrites Python"s bare
Infinity/-Infinity/NaN tokens outside string literals before retrying,
so only payloads that actually contain specials pay the extra cost.
NaN has no JSON literal and becomes null (matches JSON.stringify).
…o null

Swap bare NaN for a sentinel string before JSON.parse and revive it back
to a real NaN so Python-side float(nan) round-trips to the frontend.
@benedikt-bartscher

Copy link
Copy Markdown
Contributor Author

related #6339

@benedikt-bartscher
benedikt-bartscher requested a review from a team as a code owner April 25, 2026 22:13
Apply orjson_dumps/orjson_loads helpers to the new reflex_base
package locations after upstream's restructuring.
@benedikt-bartscher
benedikt-bartscher marked this pull request as draft April 26, 2026 16:50
benedikt-bartscher and others added 3 commits August 28, 2026 21:41
orjson.loads rounds integers outside the 64-bit range to floats, so a
customdata id could be silently corrupted when plotly serialized with
its stdlib engine.
@benedikt-bartscher
benedikt-bartscher marked this pull request as ready for review August 29, 2026 19:51

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 39 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread reflex/app.py Resolved
Comment thread pyproject.toml Resolved
Comment thread tests/units/utils/test_format_orjson.py Resolved
Comment thread tests/units/utils/test_format_orjson.py Resolved
@benedikt-bartscher

benedikt-bartscher commented Aug 29, 2026 •

Copy link
Copy Markdown
Contributor Author

blocked by #6933

edit: merged/unblocked now

Comment thread news/6116.bugfix.md Resolved
benedikt-bartscher and others added 3 commits September 18, 2026 15:46
Route main's new stateful-pages marker reader/writer through the orjson
helpers and keep both new event-processing benchmarks.
# Conflicts:
#	reflex/app.py
#	reflex/utils/frontend_skeleton.py
#	tests/benchmarks/test_event_processing.py
#	tests/units/test_prerequisites.py
#	tests/units/utils/test_exec.py
Upstream's new JSON emitters in the compile path now go through
orjson_dumps like the PR's other call sites: the stateSchema export in
app_root_template (templates.py no longer imports json) and the
useStateContext hook in VarData.from_state. The stateful pages marker
read keeps orjson_loads with upstream's ValueError comment.

The orjson test's datetime.timezone.utc becomes datetime.UTC, which ruff
now requires after upstream raised the minimum Python to 3.11.
Comment on lines +194 to +196
if not _orjson_native_equivalent and any(
isinstance(candidate, type) and issubclass(candidate, _ORJSON_NATIVE_TYPES)
for candidate in (type_, *_union_members(type_))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Shared serializers are skipped

A serializer registered for a shared base class can still be bypassed when orjson is installed. For example, register a serializer for WireValue, then define Status(WireValue, Enum). get_serializer(Status) finds that serializer, but this check only tests whether WireValue itself subclasses Enum or UUID.

The flag stays false, so outgoing updates use Status.value instead of the registered result. Account for serializers inherited through other base classes so installing the extra does not change user data.

await p.enqueue(
TOKEN,
Event(
name=(fields := orjson_loads(raw))["name"],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Benchmark measures the wrong decoder

The new wire benchmark decodes incoming events with orjson_loads, but the real socket path deliberately uses stdlib json.loads. With the extra installed, this measures a faster decoder that users do not get, so the benchmark can overstate the event-path improvement. Use _sio_loads and _sio_dumps here to measure the actual socket path.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@benedikt-bartscher

benedikt-bartscher commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor Author

#7448 seems like a better approach

@benedikt-bartscher
benedikt-bartscher marked this pull request as draft October 20, 2026 21:22

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

perf Performance-improving changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants