Skip to content

[Bug]: ZMQ _send_recv raises SaltReqTimeoutError after hard-coded 300ms POLLOUT miss instead of configured request timeout #69802

Description

@dwoz

What happened?

In salt/transport/zeromq.py, both AsyncReqMessageClient._send_recv and RequestClient._send_recv treat a single hard-coded 300ms socket.poll(..., zmq.POLLOUT) miss as an immediate request timeout:

if not await socket.poll(300, zmq.POLLOUT):
    if not future.done():
        future.set_exception(
            SaltReqTimeoutError("Socket not ready for sending")
        )
    if not self._closing:
        await self._reconnect()
    break

This produces the familiar minion log line:

Request timed out while waiting for a response. reconnecting.

even when the caller passed a much larger timeout to send() (e.g. via return_retry_timer / request_channel_timeout). That timeout is applied separately with io_loop.call_later(timeout, self._timeout_message, future), which is the intended request deadline.

Expected behavior

The 300ms value should only be a readiness poll slice (same pattern as the POLLIN loop that follows). _send_recv should keep polling for POLLOUT until either:

  1. the socket becomes write-ready and the message is sent, or
  2. the future is already done because _timeout_message fired at the configured request timeout.

It should not call future.set_exception(SaltReqTimeoutError(...)) on a POLLOUT miss before that configured deadline.

Contrast with POLLIN path (already correct)

while True:
    if future.done():
        break
    ready = await socket.poll(300, zmq.POLLIN)
    ...

Suggested fix

Mirror POLLIN:

sent = False
while not future.done():
    if await socket.poll(300, zmq.POLLOUT):
        await socket.send(message)
        sent = True
        break
if not sent:
    continue  # configured timeout already completed the future

Impact

Under master backpressure / slow REQ socket write readiness (e.g. overloaded Salt master), job results can complete on the minion but fail to publish because the return path aborts after 300ms despite return_retry_timer / request_channel_timeout being configured much higher. Raising those options has no effect on this POLLOUT path because the 300ms constant is not read from opts.

Notes

  • Observed on Salt 3008.0rc4 (not in the Major version dropdown yet); code pattern is present in current salt/transport/zeromq.py for both request client classes.
  • 300ms is not configurable via any Salt opt (zmq_backlog / pub_hwm / etc. do not apply).

Type of salt install

Official rpm (appliance-packaged Salt)

Major version

  • 3007.x (pattern also present on 3008.0rc4)

What supported OS are you seeing the problem on?

  • photon-5 (vCenter / VCF appliance minion)

salt --versions-report output

Salt: 3008.0rc4
Python: 3.14.5

(Truncated; full report available on request.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugbroken, incorrect, or confusing behavior

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions