This repository has been archived by the owner on Apr 26, 2024. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Improve
reactor_tick_time
metric (#11724)
The existing implementation of the `python_twisted_reactor_tick_time` metric is pretty useless, because it *only* measures the time taken to execute timed calls and callbacks from threads. That neglects everything that happens off the back of I/O, which is obviously quite a lot for us. To improve this, I've hooked into a different place in the reactor - in particular, where it calls `epoll`. That call is the only place it should wait for something to happen - the rest of the loop *should* be quick. I've also removed `python_twisted_reactor_pending_calls`, because I don't believe anyone ever looks at it, and it's a nuisance to populate.
- Loading branch information
Showing
5 changed files
with
86 additions
and
109 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Improve accuracy of `python_twisted_reactor_tick_time` prometheus metric. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Remove `python_twisted_reactor_pending_calls` prometheus metric. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
# Copyright 2022 The Matrix.org Foundation C.I.C. | ||
# | ||
# 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. | ||
|
||
import select | ||
import time | ||
from typing import Any, Iterable, List, Tuple | ||
|
||
from prometheus_client import Histogram, Metric | ||
from prometheus_client.core import REGISTRY, GaugeMetricFamily | ||
|
||
from twisted.internet import reactor | ||
|
||
# | ||
# Twisted reactor metrics | ||
# | ||
|
||
tick_time = Histogram( | ||
"python_twisted_reactor_tick_time", | ||
"Tick time of the Twisted reactor (sec)", | ||
buckets=[0.001, 0.002, 0.005, 0.01, 0.025, 0.05, 0.1, 0.2, 0.5, 1, 2, 5], | ||
) | ||
|
||
|
||
class EpollWrapper: | ||
"""a wrapper for an epoll object which records the time between polls""" | ||
|
||
def __init__(self, poller: "select.epoll"): | ||
self.last_polled = time.time() | ||
self._poller = poller | ||
|
||
def poll(self, *args, **kwargs) -> List[Tuple[int, int]]: # type: ignore[no-untyped-def] | ||
# record the time since poll() was last called. This gives a good proxy for | ||
# how long it takes to run everything in the reactor - ie, how long anything | ||
# waiting for the next tick will have to wait. | ||
tick_time.observe(time.time() - self.last_polled) | ||
|
||
ret = self._poller.poll(*args, **kwargs) | ||
|
||
self.last_polled = time.time() | ||
return ret | ||
|
||
def __getattr__(self, item: str) -> Any: | ||
return getattr(self._poller, item) | ||
|
||
|
||
class ReactorLastSeenMetric: | ||
def __init__(self, epoll_wrapper: EpollWrapper): | ||
self._epoll_wrapper = epoll_wrapper | ||
|
||
def collect(self) -> Iterable[Metric]: | ||
cm = GaugeMetricFamily( | ||
"python_twisted_reactor_last_seen", | ||
"Seconds since the Twisted reactor was last seen", | ||
) | ||
cm.add_metric([], time.time() - self._epoll_wrapper.last_polled) | ||
yield cm | ||
|
||
|
||
try: | ||
# if the reactor has a `_poller` attribute, which is an `epoll` object | ||
# (ie, it's an EPollReactor), we wrap the `epoll` with a thing that will | ||
# measure the time between ticks | ||
from select import epoll | ||
|
||
poller = reactor._poller # type: ignore[attr-defined] | ||
except (AttributeError, ImportError): | ||
pass | ||
else: | ||
if isinstance(poller, epoll): | ||
poller = EpollWrapper(poller) | ||
reactor._poller = poller # type: ignore[attr-defined] | ||
REGISTRY.register(ReactorLastSeenMetric(poller)) |