Skip to content

allow leak and block detection on handlers#5620

Merged
adhami3310 merged 12 commits into
mainfrom
lendemor/use_pyleak
Aug 6, 2025
Merged

allow leak and block detection on handlers#5620
adhami3310 merged 12 commits into
mainfrom
lendemor/use_pyleak

Conversation

@Lendemor
Copy link
Copy Markdown
Contributor

No description provided.

Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

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

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

Edit Code Review Bot Settings | Greptile

Comment thread reflex/monitoring/pyleak.py Outdated
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Jul 24, 2025

CodSpeed Performance Report

Merging #5620 will not alter performance

Comparing lendemor/use_pyleak (ccaba49) with main (54e0727)

Summary

✅ 8 untouched benchmarks

Comment thread reflex/monitoring/__init__.py Outdated
Comment thread pyproject.toml Outdated
@masenf
Copy link
Copy Markdown
Collaborator

masenf commented Aug 1, 2025

import asyncio

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!")


def index() -> rx.Component:
    return rx.button("bg task", on_click=State.bg_task)


app = rx.App()
app.add_page(index)

@masenf
Copy link
Copy Markdown
Collaborator

masenf commented Aug 1, 2025

also, i think we should move the module to reflex.utils.monitoring

Copy link
Copy Markdown
Collaborator

@masenf masenf left a comment

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Collaborator

@masenf masenf left a comment

Choose a reason for hiding this comment

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

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.

@adhami3310 adhami3310 merged commit 606538a into main Aug 6, 2025
41 checks passed
@adhami3310 adhami3310 deleted the lendemor/use_pyleak branch August 6, 2025 20:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants