title: AppEngine Query.fetch_async not very asynchronous?
body: <p>I'm trying to reduce the execution time of an AppEngine query by running multiple sub-queries asynchronously, using query.fetch_async(). However, it seems that the gain is minimal compared to running the queries serially.</p>
  
  <p>Below is some minimal sample code (in Python) illustrating the problem - first a function for the asynchronous run:</p>
  
  <pre><code>def run_parallel(self, repeats):
      start = datetime.utcnow()
  
      futures = []
      for i in xrange(0, repeats):
          q = User.query()
          f = q.fetch_async(300, keys_only=True)
          futures.append(f)
  
      while futures:
          f = ndb.Future.wait_any(futures)
          futures.remove(f)
          results = f.get_result()
          delta_secs = (datetime.utcnow() - start).total_seconds()
          self.response.out.write("Got %d results, delta_sec: %f&lt;br&gt;\n" %(len(results), delta_secs))
  </code></pre>
  
  <p>Then a function for the corresponding serial run:</p>
  
  <pre><code>def run_serial(self, repeats):
      start = datetime.utcnow()
      for i in xrange(0, repeats):
          q = User.query()
          results = q.fetch(300, keys_only=True)
          delta_secs = (datetime.utcnow() - start).total_seconds()
          self.response.out.write("Got %d results, delta_sec: %f&lt;br&gt;\n" %(len(results), delta_secs))
  </code></pre>
  
  <p>The output of running these two functions 10 times each (not on the dev-server), i.e. of the following calls:</p>
  
  <pre><code>run_parallel(10)
  run_serial(10)
  </code></pre>
  
  <p>is as follows:</p>
  
  <p>Running parallel queries...<br>
  Got 300 results, delta_sec: 0.401090<br>
  Got 300 results, delta_sec: 0.501700<br>
  Got 300 results, delta_sec: 0.596110<br>
  Got 300 results, delta_sec: 0.686120<br>
  Got 300 results, delta_sec: 0.709220<br>
  Got 300 results, delta_sec: 0.792070<br>
  Got 300 results, delta_sec: 0.816500<br>
  Got 300 results, delta_sec: 0.904360<br>
  Got 300 results, delta_sec: 0.993600<br>
  Got 300 results, delta_sec: 1.017320<br>
  <br>
  Running serial queries...<br>
  Got 300 results, delta_sec: 0.114950<br>
  Got 300 results, delta_sec: 0.269010<br>
  Got 300 results, delta_sec: 0.370590<br>
  Got 300 results, delta_sec: 0.472090<br>
  Got 300 results, delta_sec: 0.575130<br>
  Got 300 results, delta_sec: 0.678900<br>
  Got 300 results, delta_sec: 0.782540<br>
  Got 300 results, delta_sec: 0.883960<br>
  Got 300 results, delta_sec: 0.986370<br>
  Got 300 results, delta_sec: 1.086500<br></p>
  
  <p>Hence the parallel and serial versions take roughly the same time, around 1 second. The Appstat are as follows, where the first 10 queries are the parallel ones and the following 10 are the serial ones:</p>
  
  <p><img src="https://i.stack.imgur.com/wl1Rm.png" alt="enter image description here"></p>
  
  <p>From these stats it looks like the 10 first queries are indeed running in parallel, but that they each are taking a disproportional amount of time compared to the individual serial queries. It looks like they may be blocking somehow, waiting for each other to complete.</p>
  
  <p><strong>So my question:</strong> Is there anything wrong with my code for running asynchronous queries? Or is there an inherent limitation in the efficiency of asynchronous queries on AppEngine?</p>
  
  <p>I wondered if the behaviour could be caused by one of the following:</p>
  
  <ol>
  <li>Running asynchronous queries on the same entity type. However, a similar example using multiple different entity types shows similar results.</li>
  <li>Running identical queries, somehow locking sections of the index. However, a similar example in which each query is different (returning disjoint result sets) yields similar results.</li>
  </ol>
  
  <p>So, I'm at a bit of a loss. Any suggestions would be greatly appreciated.</p>
  
  <p><strong>Update 1</strong></p>
  
  <p>Following Bruyere's suggestion I've tried using db rather than ndb, and I've tried swapping the order of the parallel and serial versions. The results are the same.</p>
  
  <p><strong>Update 2</strong></p>
  
  <p>Here's a related post concerned with the same issue; still no answer as to why parallel queries are so inefficient:</p>
  
  <p><a href="https://stackoverflow.com/questions/11509368/best-practice-to-query-large-number-of-ndb-entities-from-datastore">Best practice to query large number of ndb entities from datastore</a></p>
  
  <p><strong>Update 3</strong></p>
  
  <p>The corresponding code using the Java SDK is parallelised very neatly. Here are the Java appstats:</p>
  
  <p><img src="https://i.stack.imgur.com/b3gnR.png" alt="enter image description here"></p>
  
  <p>To be precise, this Java implementation is explicitly multi-threaded, running queries in separate threads; this is necessary because, contrary to what the AppEngine documentation claims, using query iterators does <em>not</em> actually result in queries being executed in parallel.</p>
  
  <p>I've tried to use explicit multi-threading with synchronous query calls in the Python version, but with the same poor results as the original Python version.</p>
  
  <p>The fact that the Java version performs as expected implies that the poor Python async performance is not caused by an AppEngine CPU bottleneck.</p>
  
  <p>The only alternative explanation that I can think of is that Python's Global Interpreter Lock is causing thrashing. This is supported by the fact that decreasing the GIL check interval (using sys.setcheckinterval) exasperates the poor async performance. </p>
  
  <p>This is surprising though: the GIL shouldn't have such a severe impact given that queries are IO bound. I speculate that perhaps the RPC input buffers are small enough that async calls resume frequently during retrieval of results, which could perhaps cause GIL thrashing. I've had a look at the Python AppEngine library code, but the low-level RPC calls are made by _apphosting_runtime___python__apiproxy.MakeCall() which seems to be closed-source.</p>
  
  <p>Alas, my conclusion is that the Python AppEngine runtime is not suited for the kind of parallel querying that I require, leaving me with no other option than moving to the Java runtime. I would really like to avoid this, and so I really hope that I'm wrong and have missed something obvious. Any suggestions or pointers would be greatly appreciated.</p>
  
  <p>Thanks!</p>