Skip to content

webserver's IncidentRegistry does not work after upgrade to uvloop #8047

@pcrespov

Description

@pcrespov

We have updated to use uvloop some time ago and now that we use in the tests as well (#8014) we noticed that servicelib.aiohttp.monitor_slow_callbacks.enable does not work as expected.

This is used in setup_diagnostics to monitor incidents, whcih have an effect on the webserver healthcheck
https://github.com/itisfoundation/osparc-simcore/blob/315de3ae3c1474604208dae850e84e6bd1d642b7/services/web/server/src/simcore_service_webserver/diagnostics/plugin.py#L43-L49

So this prevents the healthcheck from working correctly!


Starting from Python 3.11, asyncio.Handle is implemented in C, and it is no longer a pure Python object. That makes monkey-patching it (e.g., replacing its __call__ method or wrapping it for debugging) much harder or even impossible in the same way it was in earlier versions.

Also, uvloop replaces the default event loop with its own C-based version, which further limits introspection and patchability.

aiodebug: log_slow_callbacks no longer works out-of-the-box

In the past, you may have done something like:

import aiodebug.log_slow_callbacks
aiodebug.log_slow_callbacks.enable(0.05)

This worked by patching asyncio.Handle.__call__ — but as of Python 3.11 and with uvloop, that method isn't patchable in Python anymore.

Workaround Options

1. Use asyncio.run with a custom debug loop and no uvloop

If debugging is your goal, you might temporarily avoid uvloop:

import asyncio
import aiodebug.log_slow_callbacks

def main():
    aiodebug.log_slow_callbacks.enable(threshold=0.05)
    asyncio.run(your_async_main())

if __name__ == '__main__':
    main()

This only works if you're not using uvloop. With uvloop, you're on a C implementation and Handle.__call__ is not patchable.

2. Use Python’s native asyncio slow callback warnings

Set the debug mode and use the built-in warning mechanism:

import asyncio

async def main():
    loop = asyncio.get_running_loop()
    loop.set_debug(True)
    # Optionally adjust warning threshold
    loop.slow_callback_duration = 0.05
    ...

asyncio.run(main())

This works with Python ≥3.7 and provides basic slow-callback detection, even with uvloop (as long as set_debug(True) is respected — uvloop supports it).

3. Patch using a custom event loop (if using pure asyncio)

If you’re not using uvloop, you could monkey-patch Handle.__call__, but with uvloop, it's not viable because the loop implementation is in C.

Summary

Approach Works with uvloop? Works with Python ≥3.11? Notes
aiodebug.log_slow_callbacks No longer patchable due to C impl
Native loop.set_debug(True) Safe and built-in
Monkey-patch Handle.__call__ ❌ (uvloop) / ⚠️ (3.10−) ❌ (3.11+) No longer viable

Recommendation

Use native asyncio slow callback logging:

async def main():
    loop = asyncio.get_running_loop()
    loop.set_debug(True)
    loop.slow_callback_duration = 0.05
    ...

Metadata

Metadata

Assignees

Labels

a:services-libraryissues on packages/service-libsa:webserverwebserver's codebase. Assigning the area is particularly useful for bugst:maintenanceSome planned maintenance work

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions