Ask Your Question
0

Why does openstack Nova use oslo_concurrency lockutils?

asked 2018-04-22 20:59:49 -0500

zero gravatar image

updated 2018-04-28 01:03:06 -0500

Hello,

I am hacking Nova code these days, I found that Nova use oslo_concurrency lockutils as a decorator like @utils.synchronized(cell_mapping.uuid). for example

def set_target_cell(context, cell_mapping):
"""Adds database connection information to the context
for communicating with the given target_cell.

This is used for permanently targeting a cell in a context.
Use this when you want all subsequent code to target a cell.

Passing None for cell_mapping will untarget the context.

:param context: The RequestContext to add connection information
:param cell_mapping: An objects.CellMapping object or None
"""

global CELL_CACHE
if cell_mapping is not None:
    # avoid circular import
    from nova import db
    from nova import rpc

    # Synchronize access to the cache by multiple API workers.
    @utils.synchronized(cell_mapping.uuid)
    def get_or_set_cached_cell_and_set_connections():
        try:
            cell_tuple = CELL_CACHE[cell_mapping.uuid]
        except KeyError:
            db_connection_string = cell_mapping.database_connection
            context.db_connection = db.create_context_manager(
                db_connection_string)
            if not cell_mapping.transport_url.startswith('none'):
                context.mq_connection = rpc.create_transport(
                    cell_mapping.transport_url)
            CELL_CACHE[cell_mapping.uuid] = (context.db_connection,
                                             context.mq_connection)
        else:
            context.db_connection = cell_tuple[0]
            context.mq_connection = cell_tuple[1]

    get_or_set_cached_cell_and_set_connections()
else:
    context.db_connection = None
    context.mq_connection = None

the code above use @utils.synchronized(cell_mapping.uuid), I confused why we use lock here to synchronized global CELL_CACHE.

I thought Nova code only use coroutines and process. The coroutines will not cause race condition right? since coroutines context switch is not controlled by OS system.

The process use OS system to control context switch, but each process has it own memory addresses, global variable is not shared by each process, therefore it should not have any race condition here right?

Do I think something wrong? please help, thanks in advance.

edit retag flag offensive close merge delete

2 answers

Sort by ยป oldest newest most voted
0

answered 2018-04-30 09:31:23 -0500

zaneb gravatar image

Nova (along with a lot of other OpenStack services) uses greenthreads via eventlet. This is sometimes referred to as 'co-routines' because that's how eventlet is implemented under the hood, but that's misleading - it's possible to write code using co-routines that only does a context switch at an explicit 'yield' (asyncio in Python 3 is designed to help with this), but eventlet is not that.

I like to refer to it as co-operative multithreading, because it works in the same way as co-operative multitasking OSs from the 90s. It's true that the OS never pre-empts a thread, but a context switch can happen at any point where you call into eventlet or code that it has monkey-patched - generally sleep() and anywhere that does I/O.

The way eventlet is sometimes marketed (co-routines!), you could be forgiven for thinking that it allows you to write multithreaded code without worrying about whether it is re-entrant, but that is unfortunately not the case. You still have to worry about whether any function you call, including 3rd-party library functions, does any I/O - or could do so in the future. It's likely that in your example, db.create_context_manager() and/or rpc.create_transport() are doing I/O and must therefore be protected with a lock to make the overall function re-entrant.

edit flag offensive delete link more
0

answered 2018-04-30 09:34:41 -0500

bnemec gravatar image

Nova does not use coroutines. It uses greenthreads from the eventlet library for concurrency within a process. They yield implicitly on IO rather than explicitly like a coroutine would (at least as I understand the difference). It does use multiple worker processes to get around the limitations of the Python GIL, but since this lock doesn't specify external=True (which would lock across processes instead of just threads) I don't think the multiple processes are relevant here.

edit flag offensive delete link more

Your Answer

Please start posting anonymously - your entry will be published after you log in or create a new account.

Add Answer

Get to know Ask OpenStack

Resources for moderators

Question Tools

2 followers

Stats

Asked: 2018-04-22 20:59:49 -0500

Seen: 162 times

Last updated: Apr 30 '18