Saturday, November 6, 2010

Speeding up pages in App Engine - Part 1

After adding numerous features to MindWell things had started to slow down a bit.  In this article I'll discuss various ways I sped up Mindwell and improved page loading times.  After going through these steps perhaps your users will thank you and start to feel like they're flying...



First a little information about Mindwell:
In MindWell there are three main models:
ClientInfo - which contains information about clients (encrypted of course).
DOS - date of service, essentially this is information about an individual session with a client.
DOSRecurr - this is a model for recurring appointments.  So if a client comes every week this is used to model those appointments.

DOS use a reference property to ClientInfo and DOSRecurr use a reference property to DOS.  So the hierarchy looks like:
ClientInfo <- DOS <- DOSRecurr

Techniques
First of all use app stats.  This will help tell you exactly how much time is dedicated to running various queries and database calls.  In my case I noticed the deadly staircase of gets which can be resolved by prefetching reference properties.  Essentially rather than iterating through a list of items you can group a bunch of objects into one get rather than a sequence of gets.  This alone made a remarkable speed up in my application.
 Below is a modified version of prefetch_refprops that ignores empty references.  Some of my objects do not set a reference to another model so I first filter out those entities.

def prefetch_refprops(entities, *props):
   non_empty_entities = [entity for entity in entities for prop in props if prop.get_value_for_datastore(entity)]
   fields = [(entity, prop) for entity in non_empty_entities for prop in props]
   ref_keys = [prop.get_value_for_datastore(x) for x, prop in fields]

   ref_entities = dict((x.key(), x) for x in db.get(set(ref_keys)))
   for (entity, prop), ref_key in zip(fields, ref_keys):
       if ref_entities[ref_key]:
         prop.__set__(entity, ref_entities[ref_key])