Skip to content

Commit b058b9e

Browse files
authored
v3.3.2 (#170)
- ReactPy Websocket will now decode messages via `orjson` resulting in an ~6% overall performance improvement. - Built-in `asyncio` event loops are now patched via `nest_asyncio`, resulting in an ~10% overall performance improvement. This has no performance impact if you are running your webserver with `uvloop`. - Fix bug where `REACTPY_WEBSOCKET_URL` always generates a warning if unset. - Fixed bug on Windows where `assert f is self._write_fut` would be raised by `uvicorn` when `REACTPY_BACKHAUL_THREAD = True`. - Fixed bug on Windows where rendering behavior would be jittery with `daphne` when `REACTPY_BACKHAUL_THREAD = True`.
1 parent 36da02e commit b058b9e

File tree

9 files changed

+71
-4
lines changed

9 files changed

+71
-4
lines changed

CHANGELOG.md

+13
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,19 @@ Using the following categories, list your changes in this order:
3636

3737
- Nothing (yet)!
3838

39+
## [3.3.2] - 2023-08-13
40+
41+
### Added
42+
43+
- ReactPy Websocket will now decode messages via `orjson` resulting in an ~6% overall performance improvement.
44+
- Built-in `asyncio` event loops are now patched via `nest_asyncio`, resulting in an ~10% overall performance improvement. This has no performance impact if you are running your webserver with `uvloop`.
45+
46+
### Fixed
47+
48+
- Fix bug where `REACTPY_WEBSOCKET_URL` always generates a warning if unset.
49+
- Fixed bug on Windows where `assert f is self._write_fut` would be raised by `uvicorn` when `REACTPY_BACKHAUL_THREAD = True`.
50+
- Fixed bug on Windows where rendering behavior would be jittery with `daphne` when `REACTPY_BACKHAUL_THREAD = True`.
51+
3952
## [3.3.1] - 2023-08-08
4053

4154
### Added

docs/python/settings.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,6 @@
2727
# 3. Your Django user model does not define a `backend` attribute
2828
REACTPY_AUTH_BACKEND = "django.contrib.auth.backends.ModelBackend"
2929

30-
# Whether to enable rendering ReactPy via a dedicated backhaul thread
31-
# This allows the webserver to process traffic while during ReactPy rendering
30+
# Whether to enable rendering ReactPy via a dedicated backhaul thread.
31+
# This allows the webserver to process traffic while during ReactPy rendering.
3232
REACTPY_BACKHAUL_THREAD = False

requirements/pkg-deps.txt

+2
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,6 @@ django >=4.1.0
33
reactpy >=1.0.0, <1.1.0
44
aiofile >=3.0
55
dill >=0.3.5
6+
orjson >=3.6.0
7+
nest_asyncio >=1.5.0
68
typing_extensions

src/reactpy_django/__init__.py

+10
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
import contextlib
2+
3+
import nest_asyncio
4+
15
from reactpy_django import checks, components, decorators, hooks, types, utils
26
from reactpy_django.websocket.paths import REACTPY_WEBSOCKET_PATH
37

@@ -11,3 +15,9 @@
1115
"utils",
1216
"checks",
1317
]
18+
# Built-in asyncio event loops can create `assert f is self._write_fut` exceptions
19+
# while we are using our backhaul thread with Uvicorn, so we use this patch to fix this.
20+
# This also resolves jittery rendering behaviors within Daphne. Can be demonstrated
21+
# using our "Renders Per Second" test page.
22+
with contextlib.suppress(ValueError):
23+
nest_asyncio.apply()

src/reactpy_django/checks.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ def reactpy_warnings(app_configs, **kwargs):
9797
)
9898

9999
# Check if REACTPY_WEBSOCKET_URL doesn't end with a slash
100-
REACTPY_WEBSOCKET_URL = getattr(settings, "REACTPY_WEBSOCKET_URL", "")
100+
REACTPY_WEBSOCKET_URL = getattr(settings, "REACTPY_WEBSOCKET_URL", "reactpy/")
101101
if isinstance(REACTPY_WEBSOCKET_URL, str):
102102
if not REACTPY_WEBSOCKET_URL or not REACTPY_WEBSOCKET_URL.endswith("/"):
103103
warnings.append(

src/reactpy_django/websocket/consumer.py

+10-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from typing import Any, MutableMapping, Sequence
1212

1313
import dill as pickle
14+
import orjson
1415
from channels.auth import login
1516
from channels.db import database_sync_to_async
1617
from channels.generic.websocket import AsyncJsonWebsocketConsumer
@@ -46,7 +47,7 @@ async def connect(self) -> None:
4647
await super().connect()
4748

4849
# Authenticate the user, if possible
49-
user: Any = self.scope.get("user")
50+
user = self.scope.get("user")
5051
if user and user.is_authenticated:
5152
try:
5253
await login(self.scope, user, backend=REACTPY_AUTH_BACKEND)
@@ -105,6 +106,14 @@ async def receive_json(self, content: Any, **_) -> None:
105106
else:
106107
await self.recv_queue.put(content)
107108

109+
@classmethod
110+
async def decode_json(cls, text_data):
111+
return orjson.loads(text_data)
112+
113+
@classmethod
114+
async def encode_json(cls, content):
115+
return orjson.dumps(content).decode()
116+
108117
async def run_dispatcher(self):
109118
"""Runs the main loop that performs component rendering tasks."""
110119
from reactpy_django import models

tests/test_app/templates/events_renders_per_second.html

+11
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ <h1>ReactPy Event Driven Renders Per Second Test Page</h1>
1717
<p>Total Active Components: <b id="total-active"></b></p>
1818
<p>Time To Load: <b id="time-to-load" data-num=0></b></p>
1919
<p>Event Renders Per Second: <b id="total-erps"></b></p>
20+
<p>Event Renders Per Second (Estimated Minimum): <b id="min-rps"></b></p>
2021
<p>Average Round-Trip Time: <b id="avg-event-rt"></b></p>
2122
</b>
2223

@@ -50,6 +51,16 @@ <h1>ReactPy Event Driven Renders Per Second Test Page</h1>
5051
}
5152
document.getElementById("total-erps").textContent = totalEPS;
5253

54+
// Calculate Min RPS
55+
let minRPS = 0;
56+
for (let i = 0; i < elements.length; i++) {
57+
let rpsValue = parseFloat(elements[i].getAttribute("data-erps"));
58+
if (rpsValue < minRPS || minRPS == 0) {
59+
minRPS = rpsValue;
60+
}
61+
}
62+
document.getElementById("min-rps").textContent = minRPS * elements.length;
63+
5364
// Calculate Average Event Round-Trip Time
5465
document.getElementById("avg-event-rt").textContent = ((1000 / totalEPS) * elements.length).toFixed(4) + " ms";
5566

tests/test_app/templates/mixed_time_to_load.html

+11
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ <h1>ReactPy IO/CPU Mixed Renders Per Second Test Page</h1>
1717
<p>Total Active Components: <b id="total-active"></b></p>
1818
<p>Time To Load: <b id="time-to-load" data-num=0></b></p>
1919
<p>Total Renders Per Second: <b id="total-rps"></b></p>
20+
<p>Total Renders Per Second (Estimated Minimum): <b id="min-rps"></b></p>
2021
</b>
2122

2223
<script>
@@ -43,6 +44,16 @@ <h1>ReactPy IO/CPU Mixed Renders Per Second Test Page</h1>
4344
}
4445
document.getElementById("total-rps").textContent = totalRPS;
4546

47+
// Calculate Min RPS
48+
let minRPS = 0;
49+
for (let i = 0; i < elements.length; i++) {
50+
let rpsValue = parseFloat(elements[i].getAttribute("data-rps"));
51+
if (rpsValue < minRPS || minRPS == 0) {
52+
minRPS = rpsValue;
53+
}
54+
}
55+
document.getElementById("min-rps").textContent = minRPS * elements.length;
56+
4657
await new Promise(resolve => setTimeout(resolve, 50));
4758
}
4859
}

tests/test_app/templates/renders_per_second.html

+11
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ <h1>ReactPy Renders Per Second Test Page</h1>
1717
<p>Total Active Components: <b id="total-active"></b></p>
1818
<p>Time To Load: <b id="time-to-load" data-num=0></b></p>
1919
<p>Total Renders Per Second: <b id="total-rps"></b></p>
20+
<p>Total Renders Per Second (Estimated Minimum): <b id="min-rps"></b></p>
2021
</b>
2122

2223
<script>
@@ -43,6 +44,16 @@ <h1>ReactPy Renders Per Second Test Page</h1>
4344
}
4445
document.getElementById("total-rps").textContent = totalRPS;
4546

47+
// Calculate Min RPS
48+
let minRPS = 0;
49+
for (let i = 0; i < elements.length; i++) {
50+
let rpsValue = parseFloat(elements[i].getAttribute("data-rps"));
51+
if (rpsValue < minRPS || minRPS == 0) {
52+
minRPS = rpsValue;
53+
}
54+
}
55+
document.getElementById("min-rps").textContent = minRPS * elements.length;
56+
4657
await new Promise(resolve => setTimeout(resolve, 50));
4758
}
4859
}

0 commit comments

Comments
 (0)