Skip to content

Commit

Permalink
Improve documentation of latency.
Browse files Browse the repository at this point in the history
Also fix #1414.
  • Loading branch information
aaugustin committed Aug 20, 2024
1 parent 8eaa5a2 commit 9e5b91b
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 16 deletions.
2 changes: 2 additions & 0 deletions docs/reference/features.rst
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ Both sides
+------------------------------------+--------+--------+--------+--------+
| Heartbeat |||||
+------------------------------------+--------+--------+--------+--------+
| Measure latency |||||
+------------------------------------+--------+--------+--------+--------+
| Perform the closing handshake |||||
+------------------------------------+--------+--------+--------+--------+
| Enforce closing timeout |||||
Expand Down
28 changes: 20 additions & 8 deletions docs/topics/keepalive.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,21 +40,24 @@ It loops through these steps:
If the Pong frame isn't received, websockets considers the connection broken and
closes it.

This mechanism serves two purposes:
This mechanism serves three purposes:

1. It creates a trickle of traffic so that the TCP connection isn't idle and
network infrastructure along the path keeps it open ("keepalive").
2. It detects if the connection drops or becomes so slow that it's unusable in
practice ("heartbeat"). In that case, it terminates the connection and your
application gets a :exc:`~exceptions.ConnectionClosed` exception.
3. It measures the :attr:`~asyncio.connection.Connection.latency` of the
connection. The time between sending a Ping frame and receiving a matching
Pong frame approximates the round-trip time.

Timings are configurable with the ``ping_interval`` and ``ping_timeout``
arguments of :func:`~asyncio.client.connect` and :func:`~asyncio.server.serve`.
Shorter values will detect connection drops faster but they will increase
network traffic and they will be more sensitive to latency.

Setting ``ping_interval`` to :obj:`None` disables the whole keepalive and
heartbeat mechanism.
heartbeat mechanism, including measurement of latency.

Setting ``ping_timeout`` to :obj:`None` disables only timeouts. This enables
keepalive, to keep idle connections open, and disables heartbeat, to support large
Expand Down Expand Up @@ -85,9 +88,23 @@ Unfortunately, the WebSocket API in browsers doesn't expose the native Ping and
Pong functionality in the WebSocket protocol. You have to roll your own in the
application layer.

Read this `blog post <https://making.close.com/posts/reliable-websockets/>`_ for
a complete walk-through of this issue.

Latency issues
--------------

The :attr:`~asyncio.connection.Connection.latency` attribute stores latency
measured during the last exchange of Ping and Pong frames::

latency = websocket.latency

Alternatively, you can measure the latency at any time by calling
:attr:`~asyncio.connection.Connection.ping` and awaiting its result::

pong_waiter = await websocket.ping()
latency = await pong_waiter

Latency between a client and a server may increase for two reasons:

* Network connectivity is poor. When network packets are lost, TCP attempts to
Expand All @@ -97,7 +114,7 @@ Latency between a client and a server may increase for two reasons:

* Traffic is high. For example, if a client sends messages on the connection
faster than a server can process them, this manifests as latency as well,
because data is waiting in flight, mostly in OS buffers.
because data is waiting in :doc:`buffers <memory>`.

If the server is more than 20 seconds behind, it doesn't see the Pong before
the default timeout elapses. As a consequence, it closes the connection.
Expand All @@ -109,8 +126,3 @@ Latency between a client and a server may increase for two reasons:

The same reasoning applies to situations where the server sends more traffic
than the client can accept.

The latency measured during the last exchange of Ping and Pong frames is
available in the :attr:`~asyncio.connection.Connection.latency` attribute.
Alternatively, you can measure the latency at any time with the
:attr:`~asyncio.connection.Connection.ping` method.
9 changes: 5 additions & 4 deletions src/websockets/asyncio/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,13 @@ def __init__(
"""
Latency of the connection, in seconds.
This value is updated after sending a ping frame and receiving a
matching pong frame. Before the first ping, :attr:`latency` is ``0``.
Latency is defined as the round-trip time of the connection. It is
measured by sending a Ping frame and waiting for a matching Pong frame.
Before the first measurement, :attr:`latency` is ``0``.
By default, websockets enables a :ref:`keepalive <keepalive>` mechanism
that sends ping frames automatically at regular intervals. You can also
send ping frames and measure latency with :meth:`ping`.
that sends Ping frames automatically at regular intervals. You can also
send Ping frames and measure latency with :meth:`ping`.
"""

# Task that sends keepalive pings. None when ping_interval is None.
Expand Down
9 changes: 5 additions & 4 deletions src/websockets/legacy/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,12 +289,13 @@ def __init__(
"""
Latency of the connection, in seconds.
This value is updated after sending a ping frame and receiving a
matching pong frame. Before the first ping, :attr:`latency` is ``0``.
Latency is defined as the round-trip time of the connection. It is
measured by sending a Ping frame and waiting for a matching Pong frame.
Before the first measurement, :attr:`latency` is ``0``.
By default, websockets enables a :ref:`keepalive <keepalive>` mechanism
that sends ping frames automatically at regular intervals. You can also
send ping frames and measure latency with :meth:`ping`.
that sends Ping frames automatically at regular intervals. You can also
send Ping frames and measure latency with :meth:`ping`.
"""

# Task running the data transfer.
Expand Down

0 comments on commit 9e5b91b

Please sign in to comment.