Skip to content

async_wrapper.py: IPC pipe timeout causes worker crash on slow IO, resulting in 30-minute poll timeout #87387

Description

@coder-wyy

Summary

When the target host experiences slow disk IO (>2.5s), async tasks fail with a 30-minute timeout instead of a clear error. The root cause is a race condition in async_wrapper.py involving three defects:

  1. IPC pipe timeout is hardcoded to 2.5s (25 × 0.1s), insufficient for slow IO environments
  2. ipc_notifier.send(True) in _run_module() is not protected by try-except, causing BrokenPipeError crash when the grandparent exits
  3. Watcher does not write failure result when worker crashes, leaving job file with finished:0 forever

Issue Type

Bug Report

Component Name

!component lib/ansible/plugins/action/async_wrapper.py

Ansible Version

ansible [core 2.15.13]
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/opt/ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /opt/ansible/lib64/python3.11/site-packages/ansible
  ansible collection location = /opt/ansible/collections:/usr/share/ansible/collections
  executable location = /opt/ansible/bin/ansible
  python version = 3.11.6 (main, Aug  1 2026, 13:00:29) [GCC 12.3.1 (openEuler 12.3.1-62.r4.hce3)] (/opt/ansible/bin/python)
  jinja version = 3.1.3
  libyaml = True

Configuration

ansible-config dump --only-changed -t all
ANSIBLE_HOME(env: ANSIBLE_HOME) = /opt/ansible
CACHE_PLUGIN(/etc/ansible/ansible.cfg) = memory
CONFIG_FILE() = /etc/ansible/ansible.cfg
DEFAULT_FORKS(/etc/ansible/ansible.cfg) = 10
DEFAULT_GATHERING(/etc/ansible/ansible.cfg) = smart
DEFAULT_HOST_LIST(/etc/ansible/ansible.cfg) = ['/etc/ansible/hosts']
DEFAULT_LOG_FILTER(/etc/ansible/ansible.cfg) = ['paramiko.transport']
DEFAULT_MANAGED_STR(/etc/ansible/ansible.cfg) = Ansible managed: {file} modified on %Y-%m-%d %H:%M:%S by {uid} on {host}
DEFAULT_NO_TARGET_SYSLOG(/etc/ansible/ansible.cfg) = True
DEFAULT_POLL_INTERVAL(/etc/ansible/ansible.cfg) = 15
DEFAULT_TIMEOUT(/etc/ansible/ansible.cfg) = 60
DEFAULT_TRANSPORT(/etc/ansible/ansible.cfg) = paramiko
DEPRECATION_WARNINGS(/etc/ansible/ansible.cfg) = False
HOST_KEY_CHECKING(/etc/ansible/ansible.cfg) = False
INTERPRETER_PYTHON(/etc/ansible/ansible.cfg) = auto_legacy_silent
PARAMIKO_HOST_KEY_AUTO_ADD(/etc/ansible/ansible.cfg) = True
PARAMIKO_LOOK_FOR_KEYS(/etc/ansible/ansible.cfg) = False
RETRY_FILES_ENABLED(/etc/ansible/ansible.cfg) = True
SYSTEM_WARNINGS(/etc/ansible/ansible.cfg) = False

CONNECTION:
==========

maglev:
______
timeout(/etc/ansible/ansible.cfg) = 60

paramiko_ssh:
____________
host_key_auto_add(/etc/ansible/ansible.cfg) = True
host_key_checking(/etc/ansible/ansible.cfg) = False
look_for_keys(/etc/ansible/ansible.cfg) = False
ssh_args(/etc/ansible/ansible.cfg) = -C -o ControlMaster=auto -o ControlPersist=1800s
timeout(/etc/ansible/ansible.cfg) = 60

ssh:
___
host_key_checking(/etc/ansible/ansible.cfg) = False
pipelining(/etc/ansible/ansible.cfg) = True
scp_if_ssh(/etc/ansible/ansible.cfg) = smart
ssh_args(/etc/ansible/ansible.cfg) = -C -o ControlMaster=auto -o ControlPersist=1800s
timeout(/etc/ansible/ansible.cfg) = 60

SHELL:
=====

sh:
__
remote_tmp(/etc/ansible/ansible.cfg) = ~/.ansible/tmp

OS / Environment

Euerle R13 x86

Steps to Reproduce

  1. On the target host, create heavy IO load to simulate slow disk: dd if=/dev/zero of=/tmp/bigfile bs=1M count=4096 oflag=dsync &

  2. Run an async task playbook:

    • name: test async task
      command: sleep 5
      async: 1800
      poll: 30
  3. Under slow IO condition, the async task may time out after 1800 seconds instead of completing normally.

This is a race condition that is hard to reproduce reliably. However,the root cause can be verified by code analysis of async_wrapper.py (see the three defects described in the issue body).

Production log evidence is provided in the issue body showing the exact sequence: grandparent IPC timeout → worker BrokenPipeError → watcher not writing failure result → 30-minute poll timeout.

Expected Results

Even when slow IO occurs, the async task should:

  1. Worker continues to execute the module regardless of whether the IPC signal was sent successfully
  2. If worker crashes, watcher should write a failure result to the job file so that poll can detect the failure promptly
  3. Poll should not blindly wait until the async timeout (1800s) when the worker has already exited abnormally

Actual Results

The async task timed out after 1800 seconds with no clear error.
Production logs from the target host:

19:02:18 ansible-async_wrapper.py[198128]: Invoked with j7332449673 1800 /root/.ansible/tmp/ansible-tmp-xxx/AnsiballZ_command.py _
19:02:18 ansible-async_wrapper.py[198132]: Starting module and watcher
19:02:18 ansible-async_wrapper.py[198132]: Start watching [198133] (1800)
19:02:18 ansible-async_wrapper.py[198133]: Start module [198133]
19:02:21 ansible-async_wrapper.py[198128]: Return async_wrapper task started.
19:02:23 kernel: Slow IO detected: python3.11-198133 vda stage bio RM 16795136 + 0 cgroup /system.slice/crond.service started 4672 ms ago threshold 1000 ms
19:02:23 ansible-async_wrapper.py[198133]: error: [Errno 32] Broken pipe
19:02:23 ansible-async_wrapper.py[198132]: Done in kid B.

The job file stayed at {"started":1, "finished":0} and poll
kept waiting until the 1800s async timeout.

Code of Conduct

  • I agree to follow the Ansible Code of Conduct

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    affects_2.15bugThis issue/PR relates to a bug.has_prThis issue has an associated PR.needs_verifiedThis issue needs to be verified/reproduced by maintainer

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions