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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions