Skip to content

wait_for_tasks: use property collector to monitor multiple tasks at once#48

Open
leust wants to merge 1 commit intostable/2023.1-m3from
tasks-property-collector
Open

wait_for_tasks: use property collector to monitor multiple tasks at once#48
leust wants to merge 1 commit intostable/2023.1-m3from
tasks-property-collector

Conversation

@leust
Copy link
Copy Markdown

@leust leust commented Dec 17, 2025

The current wait_for_task implementation polls each task individually by creating separate SOAP requests repeatedly. This is inefficient when a service manages many concurrent tasks, as each poll generates CPU load and network traffic on both the client and vCenter server.

This change implements a centralized property collector that uses VMware's WaitForUpdatesEx API to monitor multiple tasks efficiently over a single persistent socket connection.

  • Runs a background thread that manages the property collector and processes task updates asynchronously
  • Uses WaitForUpdatesEx to wait for task state changes instead of polling, reducing SOAP requests.
  • Maintains 1 TaskFilter per task (create/delete).
  • Automatically recreates the property collector on errors

The feature is opt-in via the use_property_collector_for_tasks constructor parameter (default: False).

The wait_for_task() method signature remains unchanged, making the optimization transparent to callers.

Change-Id: Iebffb74a53a7fa8d679daeb486b844fdea5b5113

@leust leust force-pushed the tasks-property-collector branch from 67d5127 to 9504b27 Compare December 18, 2025 08:29
Copy link
Copy Markdown
Member

@fwiesel fwiesel left a comment

Choose a reason for hiding this comment

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

Couple of things:

I'd avoid eventlet primitives and go straight for the threading ones. Eventlet will hopefully gone soon.

I've added some small corrections, but I have also a larger problem with how much context (functions and variables) are shared between the threads.

In my mind, it should be sufficient to have one synchronized queue to communicate with the thread. I'd setup both of them in __init__ and stop the thread in __del__.
While Nova does call logout() before terminating (and only then), Cinder doesn't.

As you need to create the property filters in the monitoring thread with _recreate_property_collector, I'd move the whole creation (and deletion) of the filters into that thread and as many variables as possible to local variables of the function

So, something along the lines of:

class VMwareAPISession(object):
    def _wait_for_task_with_property_collector(self, task):
      ctx = context.get_current()
      future = futures.Future()
      task_value = vim_util.get_moref_value(task)
      self._pending_tasks.put((task_value, future, ctx))
      return future.result()

   def __del__(self):
      self._pending_tasks.shutdown(immediate=True)

    def _property_collector_loop(self):
        property_collector = ...
        property_collector_version = ""
        # Maps task moref value -> (future, context, filter) for running tasks
        running_tasks = {}
        ...
                if pending_task is not None:
                    task_value, future, ctx = pending_task
                    task_filter = self._create_task_filter(task_value)
                    self._running_tasks[task_value] = (future, ctx,
                                                       task_filter)
                    LOG.debug("Received task %s, moved to running.",
                              task_value)
        ...

Comment thread oslo_vmware/api.py Outdated
Comment on lines +309 to +310
self._property_collector_thread = eventlet.spawn(
self._property_collector_loop)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Here, I'd go with

self._property_collector_thread = threading.Thread(target=self._property_collector_loop)
self._property_collector_thread.start()

Comment thread oslo_vmware/api.py Outdated
Comment thread oslo_vmware/api.py Outdated
Comment thread oslo_vmware/api.py

def _start_property_collector_thread(self):
"""Start background property collector thread for task monitoring."""
with self._property_collector_lock.write_lock():
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If I see it correctly _start_property_collector_thread is only called through _create_session, which itself is using a lock, so we can't have concurrent calls of _start_property_collector_thread.

I see that the corresponding logout() which calls _stop_property_collector_thread is not taking that same lock, which doesn't sit right with me.

I'd rather have the same lock being taken in _create_session and logout() to ensure that both the session as well the thread will be closed atomically.

It didn't make a difference, as long python wasn't really multi-threaded.

Comment thread oslo_vmware/api.py Outdated
Comment thread oslo_vmware/api.py Outdated
Comment thread oslo_vmware/api.py Outdated
Comment thread oslo_vmware/api.py Outdated
Comment thread oslo_vmware/api.py Outdated
Comment thread oslo_vmware/api.py Outdated
Comment on lines +574 to +577
future.set_exception(
exceptions.VimException(
_("Property collector stopped while waiting for "
"task %s") % task_value))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why no try/except here, but above?

@leust leust force-pushed the tasks-property-collector branch from 9504b27 to 35fe834 Compare January 21, 2026 09:40
@leust leust requested a review from fwiesel January 22, 2026 09:23
The current wait_for_task implementation polls each task individually
by creating separate SOAP requests repeatedly. This is inefficient when
a service manages many concurrent tasks, as each poll generates CPU load
and network traffic on both the client and vCenter server.

This change implements a centralized property collector that uses
VMware's WaitForUpdatesEx API to monitor multiple tasks efficiently
over a single persistent socket connection.

- Runs a background thread that manages the property collector and
  processes task updates asynchronously
- Uses WaitForUpdatesEx to wait for task state changes instead of
  polling, reducing SOAP requests.
- Maintains 1 TaskFilter per task (create/delete).
- Automatically recreates the property collector on errors

The feature is opt-in via the use_property_collector_for_tasks
constructor parameter (default: False).

The wait_for_task() method signature remains unchanged,
making the optimization transparent to callers.

Change-Id: Iebffb74a53a7fa8d679daeb486b844fdea5b5113
@leust leust force-pushed the tasks-property-collector branch from 35fe834 to c9868f0 Compare February 18, 2026 09:00
@leust
Copy link
Copy Markdown
Author

leust commented Mar 4, 2026

@fwiesel may I have your 2nd review on this? Thanks.

@fwiesel
Copy link
Copy Markdown
Member

fwiesel commented Mar 4, 2026

You've addressed the rather cosmetic changes, but not my main issue, the part that comes after:

but I have also a larger problem with how much context (functions and variables) are shared between the threads.

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