829e822581
* Initialization of profiler was also missing for a thread spawned within post_tx_queue.py so we were loosing important profiling info * Changed the profiler test since its logic was already obsolete. Now we initialize profiler in every thread so the only reason to not get any profiler traces when a workflow completed is "enabled = False" in the "profiler" group in the configuration. * Added more profiler traces * Small readability changes in the workflow language spec Change-Id: I35e6711f8e10bb08d7e842f4bca8753b929328fd
138 lines
3.8 KiB
Python
138 lines
3.8 KiB
Python
# Copyright 2015 - Mirantis, Inc.
|
|
# Copyright 2016 - Brocade Communications Systems, Inc.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
import eventlet
|
|
import functools
|
|
from oslo_config import cfg
|
|
from oslo_log import log as logging
|
|
from osprofiler import profiler
|
|
|
|
from mistral import context
|
|
from mistral.db import utils as db_utils
|
|
from mistral.db.v2 import api as db_api
|
|
from mistral_lib import utils
|
|
|
|
|
|
"""
|
|
This module contains a mini framework for scheduling operations while
|
|
performing transactional processing of a workflow event such as
|
|
completing a workflow action. The scheduled operations will run after
|
|
the main DB transaction, in a new transaction, if needed.
|
|
"""
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
_THREAD_LOCAL_NAME = "__operation_queue_thread_local"
|
|
|
|
|
|
def _prepare():
|
|
# Register two queues: transactional and non transactional operations.
|
|
utils.set_thread_local(_THREAD_LOCAL_NAME, (list(), list()))
|
|
|
|
|
|
def _clear():
|
|
utils.set_thread_local(_THREAD_LOCAL_NAME, None)
|
|
|
|
|
|
def register_operation(func, args=None, in_tx=False):
|
|
"""Register an operation."""
|
|
|
|
_get_queues()[0 if in_tx else 1].append((func, args or []))
|
|
|
|
|
|
def _get_queues():
|
|
queues = utils.get_thread_local(_THREAD_LOCAL_NAME)
|
|
|
|
if queues is None:
|
|
raise RuntimeError(
|
|
'Operation queue is not initialized for the current thread.'
|
|
' Most likely some engine method is not decorated with'
|
|
' operation_queue.run()'
|
|
)
|
|
|
|
return queues
|
|
|
|
|
|
def run(func):
|
|
"""Decorator that runs all operations registered in the operation queue.
|
|
|
|
Various engine methods may register such operations. All such methods must
|
|
be decorated with this decorator.
|
|
"""
|
|
@functools.wraps(func)
|
|
def decorate(*args, **kw):
|
|
_prepare()
|
|
|
|
try:
|
|
res = func(*args, **kw)
|
|
|
|
queues = _get_queues()
|
|
|
|
tx_queue = queues[0]
|
|
non_tx_queue = queues[1]
|
|
|
|
if not tx_queue and not non_tx_queue:
|
|
return res
|
|
|
|
auth_ctx = context.ctx() if context.has_ctx() else None
|
|
|
|
def _within_new_thread():
|
|
# This is a new thread so we need to init a profiler again.
|
|
if cfg.CONF.profiler.enabled:
|
|
profiler.init(cfg.CONF.profiler.hmac_keys)
|
|
|
|
old_auth_ctx = context.ctx() if context.has_ctx() else None
|
|
|
|
context.set_ctx(auth_ctx)
|
|
|
|
try:
|
|
if tx_queue:
|
|
_process_tx_queue(tx_queue)
|
|
|
|
if non_tx_queue:
|
|
_process_non_tx_queue(non_tx_queue)
|
|
finally:
|
|
context.set_ctx(old_auth_ctx)
|
|
|
|
eventlet.spawn(_within_new_thread)
|
|
finally:
|
|
_clear()
|
|
|
|
return res
|
|
|
|
return decorate
|
|
|
|
|
|
@db_utils.retry_on_db_error
|
|
@run
|
|
def _process_tx_queue(queue):
|
|
with db_api.transaction():
|
|
for func, args in queue:
|
|
try:
|
|
func(*args)
|
|
except Exception:
|
|
LOG.exception("Failed to run transactional engine operation.")
|
|
|
|
raise
|
|
|
|
|
|
def _process_non_tx_queue(queue):
|
|
for func, args in queue:
|
|
try:
|
|
func(*args)
|
|
except Exception:
|
|
LOG.exception("Failed to run non-transactional engine operation.")
|