Skip to content

honor recv_timeout in _sock_exact_recv() and ping() #190

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 20, 2024

Conversation

vladak
Copy link
Contributor

@vladak vladak commented Nov 26, 2023

This logical change uses recv_timeout as receive timeout in _sock_exact_recv().

Casually tested with CPython using the test case supplied in the bug, more testing will follow.

@vladak vladak marked this pull request as draft November 28, 2023 09:17
@vladak vladak force-pushed the recv_timeout_vs_keep_alive branch from 70eba85 to 149f040 Compare January 19, 2024 21:05
@vladak vladak marked this pull request as ready for review January 19, 2024 21:05
@vladak vladak force-pushed the recv_timeout_vs_keep_alive branch from 149f040 to b99707a Compare January 19, 2024 21:06
@vladak
Copy link
Contributor Author

vladak commented Jan 20, 2024

Added a simple test case.

@vladak
Copy link
Contributor Author

vladak commented Jan 20, 2024

Tested on QtPy running Adafruit CircuitPython 8.2.6 on 2023-09-12; Adafruit QT Py ESP32-S3 no psram with ESP32S3 with the code from the issue adapted for CP:

#!/usr/bin/env python3

import ssl
import time
import wifi
import socketpool

import adafruit_minimqtt.adafruit_minimqtt as MQTT

import adafruit_logging as logging

from secrets import secrets


def main():
    logger = logging.getLogger(__name__)
    logger.setLevel(logging.DEBUG)

    logger.info(f"Connecting to wifi {secrets['SSID']}")
    wifi.radio.connect(secrets["SSID"], secrets["password"], timeout=20)
    logger.info(f"Connected to {secrets['SSID']}")
    logger.debug(f"IP: {wifi.radio.ipv4_address}")

    pool = socketpool.SocketPool(wifi.radio)

    keep_alive_timeout = 16
    host = "172.40.0.3"
    port = 1883
    mqtt_client = MQTT.MQTT(
        broker=host,
        port=port,
	socket_pool=pool,
        ssl_context=ssl.create_default_context(),
        connect_retries=1,
        recv_timeout=5,
        keep_alive=keep_alive_timeout,
    )

    mqtt_client.logger = logger

    logger.debug(f"connecting with keep alive = {keep_alive_timeout}")
    mqtt_client.connect()

    delay = 2 * keep_alive_timeout
    logger.debug(f"sleeping for {delay} seconds")
    time.sleep(delay)
    logger.debug("pinging the server")
    try:
        mqtt_client.ping()
    except MQTT.MMQTTException as e:
        logger.error(f"ping failed: {e}")


if __name__ == "__main__":
    main()

the output was as follows:

1311.760: INFO - Connecting to wifi XXX
1311.761: INFO - Connected to XXX
1311.763: DEBUG - IP: 172.40.0.23
1311.766: DEBUG - connecting with keep alive = 16
1311.767: DEBUG - Attempting to connect to MQTT broker (attempt #0)
1311.768: DEBUG - Attempting to establish MQTT connection...
1311.770: INFO - Establishing an INSECURE connection to 172.40.0.3:1883
1311.775: DEBUG - Sending CONNECT to broker...
1311.777: DEBUG - Fixed Header: bytearray(b'\x10\x14')
1311.778: DEBUG - Variable Header: bytearray(b'\x00\x04MQTT\x04\x02\x00\x10')
1311.782: DEBUG - Receiving CONNACK packet from broker
1311.785: DEBUG - Got message type: 0x20 pkt: 0x20
1311.787: DEBUG - Resetting reconnect backoff
1311.789: DEBUG - sleeping for 32 seconds
1343.790: DEBUG - pinging the server
1343.791: DEBUG - Sending PINGREQ
1348.794: ERROR - ping failed: PINGRESP not returned from broker within 5 seconds.

On CP it works differently than with CPython - the timeout from _sock_exact_recv() is propagated via _wait_for_msg() as None into the higher level functions which then check the timeout themselves, in this case this is ping(). The important part is that the time difference between the PINREQ sent and the detection of PINGRESP not received matches the receive timeout, not keep alive timeout.

@vladak
Copy link
Contributor Author

vladak commented Jan 20, 2024

Checking the code after this change for the remaining keep_alive occurrences, they are only in places related to the CONNECT message marshalling and inside the loop() in order to ping the server periodically.

@vladak
Copy link
Contributor Author

vladak commented Jan 21, 2024

The change in ping() probably deserves some comment. The MQTT 3.1.1 spec in section 2.2 says:

If a client does not receive a PINGRESP message within a Keep Alive time period after sending a PINGREQ, it should close the TCP/IP socket connection.

The current code in ping() merely raises the MMQTTException exception without closing the connection, perhaps with the expectation that the caller will close it. Anyhow, I think the receive timeout should trump the keep alive timeout also in this case - the information about not being able to receive data should get to the caller as soon as possible (assuming receive timeout is usually much smaller than the keep alive timeout).

@vladak vladak changed the title honor recv_timeout in _sock_exact_recv() honor recv_timeout in _sock_exact_recv() and ping() Jan 25, 2024
Copy link
Contributor

@dhalbert dhalbert left a comment

Choose a reason for hiding this comment

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

This looks good to me.

@brentru Does this cause any issues? @vladak mentioned elsewhere it fixes some other issues people are seeing.

@dhalbert
Copy link
Contributor

@vladak this now has merge conflicts.

@vladak vladak force-pushed the recv_timeout_vs_keep_alive branch from 0eb4404 to 5814fd0 Compare March 27, 2024 16:28
@vladak
Copy link
Contributor Author

vladak commented Mar 27, 2024

@vladak this now has merge conflicts.

rebased

Copy link
Contributor

@dhalbert dhalbert left a comment

Choose a reason for hiding this comment

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

Let's go ahead and get this in. Sorry for the delay.

@dhalbert dhalbert merged commit 53b1412 into adafruit:main May 20, 2024
adafruit-adabot added a commit to adafruit/Adafruit_CircuitPython_Bundle that referenced this pull request May 21, 2024
Updating https://github.com/adafruit/Adafruit_CircuitPython_MiniMQTT to 7.8.0 from 7.7.0:
  > Merge pull request adafruit/Adafruit_CircuitPython_MiniMQTT#190 from vladak/recv_timeout_vs_keep_alive
  > Merge pull request adafruit/Adafruit_CircuitPython_MiniMQTT#213 from justmobilize/no-retry-on-unauthorized

Updating https://github.com/adafruit/Adafruit_CircuitPython_Bundle/circuitpython_library_list.md to NA from NA:
  > Updated download stats for the libraries
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.

2 participants