Since the two refreshes are completely independent, the simplest approach is to just run both ansible-playbook commands in parallel from a wrapper script:
ansible-playbook refresh_a_from_b.yml &
ansible-playbook refresh_d_from_c.yml &
wait
The & backgrounds each command and wait blocks until both finish. Total time becomes the length of the slower one rather than both added together.
If you want to keep everything in a single playbook, you can use async with poll: 0 to fire and forget each task, then poll for completion later:
- name: Start refresh A from B
command: /path/to/refresh_a.sh
async: 14400
poll: 0
register: refresh_a
- name: Start refresh D from C
command: /path/to/refresh_d.sh
async: 14400
poll: 0
register: refresh_d
- name: Wait for refresh A
async_status:
jid: "{{ refresh_a.ansible_job_id }}"
register: result_a
until: result_a.finished
retries: 480
delay: 30
- name: Wait for refresh D
async_status:
jid: "{{ refresh_d.ansible_job_id }}"
register: result_d
until: result_d.finished
retries: 480
delay: 30
Set async high enough to cover your longest expected run time (14400 = 4 hours). The poll: 0 means Ansible starts the task and moves on immediately. The async_status tasks then wait for each job to complete.
The bash wrapper approach is generally more straightforward for independent playbooks, but the async approach is useful if you need Ansible to track the results and handle errors within the same run.