allow leak and block detection on handlers#5620
Conversation
There was a problem hiding this comment.
Greptile Summary
This PR integrates PyLeak monitoring capabilities into Reflex to detect event loop blocking and resource leaks (thread and task leaks) during event handler execution. The implementation adds a new reflex/monitoring module that provides a clean API for leak detection functionality through four main functions: is_pyleak_enabled, monitor_async, monitor_sync, and monitor_leaks.
The core integration happens in reflex/state.py where event handlers are conditionally wrapped with the @monitor_leaks decorator when PyLeak monitoring is enabled via configuration. The monitoring system supports both synchronous and asynchronous event handlers, automatically detecting the function type and applying the appropriate monitoring context.
Configuration is handled through new fields in BaseConfig including enable_pyleak_monitoring (boolean flag), pyleak_blocking_threshold (float for detecting blocking operations), pyleak_thread_grace_period (float for thread cleanup timing), and pyleak_action (enum defining what happens when leaks are detected). The PyLeak dependency (version >=0.1.14,<1.0) has been added to pyproject.toml.
This monitoring system is designed as an optional development/debugging tool that adds zero overhead when disabled, making it safe for production environments while providing valuable insights during development for detecting performance bottlenecks and resource management issues in Reflex applications.
Confidence score: 4/5
• This PR appears safe to merge with minimal risk of production issues since monitoring is disabled by default
• The implementation follows good practices with conditional imports and zero-overhead when disabled, though the integration touches core event handling logic
• Files reflex/state.py and reflex/monitoring/pyleak.py need closer attention due to their integration with core event handling and complex monitoring logic
5 files reviewed, 1 comment
CodSpeed Performance ReportMerging #5620 will not alter performanceComparing Summary
|
Add overloads to `monitor_leaks` to avoid pyright ignores Remove extra layer of callable in `monitor_leaks`
|
|
also, i think we should move the module to |
masenf
left a comment
There was a problem hiding this comment.
New repro code
import asyncio
import time
import reflex as rx
class State(rx.State):
@rx.event(background=True)
async def bg_task(self):
return [
State.another_bg_task,
State.quick_thing,
]
@rx.event(background=True)
async def another_bg_task(self):
await asyncio.sleep(1)
@rx.event
def quick_thing(self):
print("Quick thinking event triggered!")
time.sleep(2)
def index() -> rx.Component:
return rx.vstack(
rx.button("bg task", on_click=State.bg_task),
rx.button("quick thing", on_click=State.quick_thing),
)
app = rx.App()
app.add_page(index)from pyleak.base import LeakAction
import reflex as rx
config = rx.Config(
app_name="repro_pyleak",
plugins=[
rx.plugins.SitemapPlugin(),
rx.plugins.TailwindV4Plugin(),
],
enable_pyleak_monitoring=True,
pyleak_action=LeakAction.LOG,
)When I click the "bg task" button, i end up seeing two event loop block detection logs.
When I click the "quick thing" button, i only see one event loop block detections.
I think we need some protection to avoid re-entering the block detection context if it is already active.
masenf
left a comment
There was a problem hiding this comment.
working well for me.
from a code organization perspective, implementing the set/reset of monitoring_active as a contextmanager itself would have fit nicely in the existing ExitStack/AsyncExitStack pattern without requiring the outer try/finally. but we can take this and refine it later if we so desire, it's a self-contained module in what amounts to debug code, so not a big deal.
No description provided.