66 lines
2.5 KiB
Python
66 lines
2.5 KiB
Python
# Adapted from [aiodebug](https://gitlab.com/quantlane/libs/aiodebug)
|
|
|
|
# Copyright 2016-2022 Quantlane s.r.o.
|
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
# Modifications:
|
|
# - Removed the dependency to `logwood`.
|
|
# - Renamed `monitor_loop_lag.enable()` to just `enable_monitor_loop_lag()`.
|
|
# - Miscellaneous changes to make it work with Ray.
|
|
|
|
import asyncio
|
|
import asyncio.events
|
|
from typing import Callable, Optional, Set
|
|
|
|
# Strong references to background tasks to prevent them from being garbage
|
|
# collected mid-execution. The asyncio event loop only keeps weak references
|
|
# to tasks, so a task with no other strong references can be collected at
|
|
# any time. See https://docs.python.org/3/library/asyncio-task.html#asyncio.create_task
|
|
_BACKGROUND_TASKS: Set[asyncio.Task] = set()
|
|
|
|
|
|
def enable_monitor_loop_lag(
|
|
callback: Callable[[float], None],
|
|
interval_s: float = 0.25,
|
|
loop: Optional[asyncio.AbstractEventLoop] = None,
|
|
) -> None:
|
|
"""Start logging event loop lags to the callback.
|
|
|
|
In ideal circumstances they should be very close to zero. Lags may increase
|
|
if event loop callbacks block for too long.
|
|
|
|
Note: this works for all event loops, including uvloop.
|
|
|
|
Args:
|
|
callback: Callback to call with the lag in seconds.
|
|
interval_s: How often to measure the lag, in seconds.
|
|
loop: The event loop to monitor. If None, uses the running loop.
|
|
"""
|
|
if loop is None:
|
|
loop = asyncio.get_running_loop()
|
|
if loop is None:
|
|
raise ValueError("No provided loop, nor running loop found.")
|
|
|
|
async def monitor():
|
|
while loop.is_running():
|
|
t0 = loop.time()
|
|
await asyncio.sleep(interval_s)
|
|
lag = loop.time() - t0 - interval_s # Should be close to zero.
|
|
callback(lag)
|
|
|
|
task = loop.create_task(monitor(), name="async_utils.monitor_loop_lag")
|
|
# Keep a strong reference so the task isn't garbage collected mid-execution.
|
|
_BACKGROUND_TASKS.add(task)
|
|
task.add_done_callback(_BACKGROUND_TASKS.discard)
|