2
votes

I'm using python runtime on The App Engine Standard Environment.

I'm using some async operations in my code, quotes from tutorial:

  • You might be able to speed up your application by performing Datastore actions in parallel with other things, or performing a few Datastore actions in parallel with each other.

  • Tasklets are a way to write concurrent functions without threads; tasklets are executed by an event loop

https://cloud.google.com/appengine/docs/python/ndb/async

It's all about parallel execution, but I don't understand how does this parallelism work in synchronous python code.

How does eventloop work? Does it run in separate process/thread? Does it perform any really-concurrent executions? Or it is just convenient way to create batched rpcs, which run synchronously on first get_result() call?

https://cloud.google.com/appengine/docs/python/refdocs/modules/google/appengine/ext/ndb/eventloop

1

1 Answers

1
votes

As you suspected async op is just a way to create and launch multiple RPCs (which may or may not be batched, it's not mandatory) before blocking on the first (and each of all subsequent) get_result() calls. And this applies to most/all of the RPC-based GAE infra (ndb, memcache, urlfetch, etc)

If you look at the implementations of the non-async versions of most of the calls (which support async versions) you'll see they're really just the async version calls immediately followed by the get_result() call, for example:

  @classmethod
  def _get_or_insert(*args, **kwds):
    """Transactionally retrieves an existing entity or creates a new one.    
    (snip)          
    """
    cls, args = args[0], args[1:]
    return cls._get_or_insert_async(*args, **kwds).get_result()
  get_or_insert = _get_or_insert

As for running the event loop itself - it looks like it is launched transparently when your app makes your first ndb RPC call (of the request/thread), via get_event_loop():

def get_event_loop():
  """Return a EventLoop instance.

  A new instance is created for each new HTTP request.  We determine
  that we're in a new request by inspecting os.environ, which is reset
  at the start of each request.  Also, each thread gets its own loop.
  """
  ev = _state.event_loop
  if not os.getenv(_EVENT_LOOP_KEY) and ev is not None:
    ev.clear()
    _state.event_loop = None
    ev = None
  if ev is None:
    ev = EventLoop()
    _state.event_loop = ev
    os.environ[_EVENT_LOOP_KEY] = '1'
  return ev