<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>Henri's Blog</title>
	<atom:link href="http://henritersteeg.wordpress.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://henritersteeg.wordpress.com</link>
	<description>Software development observations</description>
	<lastBuildDate>Mon, 30 Mar 2009 13:49:53 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
<cloud domain='henritersteeg.wordpress.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://s2.wp.com/i/buttonw-com.png</url>
		<title>Henri's Blog</title>
		<link>http://henritersteeg.wordpress.com</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://henritersteeg.wordpress.com/osd.xml" title="Henri&#039;s Blog" />
	<atom:link rel='hub' href='http://henritersteeg.wordpress.com/?pushpress=hub'/>
		<item>
		<title>Generic DB caching in Google App Engine</title>
		<link>http://henritersteeg.wordpress.com/2009/03/30/generic-db-caching-in-google-app-engine/</link>
		<comments>http://henritersteeg.wordpress.com/2009/03/30/generic-db-caching-in-google-app-engine/#comments</comments>
		<pubDate>Mon, 30 Mar 2009 13:30:20 +0000</pubDate>
		<dc:creator>Henri</dc:creator>
				<category><![CDATA[Google App Engine]]></category>
		<category><![CDATA[GAE]]></category>

		<guid isPermaLink="false">http://henritersteeg.wordpress.com/?p=100</guid>
		<description><![CDATA[In my previous post, I described a caching solution based on subclassing the db.ReferenceProperty class. This time I want to take this one step further and implement this on the lower level API: apiproxy_stub_map.apiproxy. You can add hooks to the datastore, as I already showed in a previous (profiling) post. However, in order to return [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=henritersteeg.wordpress.com&amp;blog=6822465&amp;post=100&amp;subd=henritersteeg&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>In my previous post, I described a caching solution based on subclassing the <code>db.ReferenceProperty</code> class. This time I want to take this one step further and implement this on the lower level API: <code>apiproxy_stub_map.apiproxy</code>.</p>
<p>You can add hooks to the datastore, as I already showed in a previous (profiling) post. However, in order to return a cached entity instead of one retrieved from the datastore, we need more than a PreCall hook. From a PreCall hook will can not prevent the actual datastore call being made. Maybe we can &#8220;trick&#8221; MakeSyncCall by removing the keys from the argument list, but that is not a road I want to explore&#8230;</p>
<p><span id="more-100"></span><br />
So, we need to replace the actual <code>apiproxy.MakeSyncCall</code>. Please be aware that there already may be references to the original MakeSyncCall, as described in <a href="http://blog.appenginefan.com/2009/01/hacking-google-app-engine-part-2.html">this post</a>. For our caching solution, this is not a real problem: the worst thing that could happen, is that there will be no caching. So, the app will be slower, but still functioning the same&#8230;</p>
<h3>Replacing a registered datastore Stub</h3>
<p>Replacing the registered datastore stub may seem like the way to go, if you want to wrap the <strong>datastore_v3</strong> call. However, registering a stub is allowed only when there is no stub registered yet. And, you guessed it, in production there is already a stub present. This method therefore, is only useful for unit testing and not for production. We could replace the global MakeSyncCall, but then we add overhead for <strong>every remote call</strong>, which is something I do not prefer.</p>
<h3>Replacing db.get</h3>
<p>If we want to retrieve a model by using a <strong>key</strong> or <strong>key_name</strong>, all calls to the datastore are implemented using the global module function <strong>db.get</strong>. So, if we can wrap this call with a caching function, we&#8217;re done. Here&#8217;s the solution I came up with:</p>
<p><pre class="brush: python;">
# define a global, weakly referenced CACHE...
_cache = weakref.WeakValueDictionary({})

def getCached(real_func, keys):
    real_keys, multiple = datastore.NormalizeAndTypeCheckKeys(keys)
    if multiple: # too difficult for now ;-)
        return real_func(keys)

    real_key = real_keys[0]
    instance = _cache.get(real_key, None)
    if instance:
        return instance
    model = real_func(keys)
    if model:
        _cache[real_key] = model
    return model

# Patch db.get
from functools import partial
db.get = partial(getCached, db.get)
</pre></p>
<p>A global function <strong>getCached</strong> is defined, which wraps the original <strong>db.get</strong> function. First, we check whether the function is called for multiple keys. If that is the case, we simply call the original function (implementing this would be too much code for this example). Otherwise, we check whether the key is already present in cache. If so we return that value, otherwise the real db.get is called and the result is added to the cache.</p>
<h3>Query results</h3>
<p>How about query results? Are they added to the cache as well? Unfortunately, they are not. When query results are fetched, the entire entity is retrieved at once, so there is no need to get the values by key. It would be nice if we could add the retrieved entities to the cache, so that we don&#8217;t have to retrieve them again for a ReferenceProperty value.</p>
<p>This turns out to be more difficult. We could subclass <strong>db.Query</strong> (and db.GqlQuery) so that we could implement a different iterator (a caching subclass of <strong>db._QueryIterator</strong>), but that seems a bit tedious. Besides, we would have to override more functions, since the model values are not always retrieved using the iterator. The <strong>fetch</strong> function, for example, uses a <strong>map</strong> function to run the entities over the <strong>Model.from_entity</strong> function.</p>
<h3>Replacing Model.from_entity</h3>
<p>Given the fact that entities are converted to Model instances by the <strong>Model.from_entity</strong> function, gives us a nice hook to implement a caching strategy, doesn&#8217;t it?</p>
<p>All we have to do, is subclass <strong>db.Model</strong> and re-implement the <strong>from_entity</strong> function:</p>
<p><pre class="brush: python;">
class CachingModel(db.Model):

    @classmethod
    def from_entity(cls, entity):
        model = super(CachingModel,cls).from_entity(entity)
        if model:
            _cache[model.key()] = model
        return model
</pre></p>
<p>The resulting <strong>CachingModel</strong> class is the one we subclass to implement our business models, like this:</p>
<p><pre class="brush: python;">
class Team(CachingModel):
    club     = db.ReferenceProperty(Club)
    naam     = db.StringProperty()
    poule    = db.ReferenceProperty(Poule)
</pre></p>
<p>If we add all model instances to the cache in the <strong>from_entity</strong> function, we can remove that from our global <strong>getCached</strong> function&#8230;</p>
<p><pre class="brush: python;">
def getCached(real_func, keys):
    real_keys, multiple = datastore.NormalizeAndTypeCheckKeys(keys)
    if multiple: # too difficult for now ;-)
        return real_func(keys)

    real_key = real_keys[0]
    instance = _cache.get(real_key, None)
    if instance:
        return instance
    model = real_func(keys)
#   if model:
#       _cache[real_key] = model
    return model
</pre></p>
<p>We now have a complete, in-memory, caching solution, which can enhance our application performance significantly with a minimal effort!</p>
<br />Posted in Google App Engine Tagged: GAE, Google App Engine <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/henritersteeg.wordpress.com/100/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/henritersteeg.wordpress.com/100/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/henritersteeg.wordpress.com/100/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/henritersteeg.wordpress.com/100/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/henritersteeg.wordpress.com/100/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/henritersteeg.wordpress.com/100/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/henritersteeg.wordpress.com/100/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/henritersteeg.wordpress.com/100/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/henritersteeg.wordpress.com/100/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/henritersteeg.wordpress.com/100/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/henritersteeg.wordpress.com/100/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/henritersteeg.wordpress.com/100/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/henritersteeg.wordpress.com/100/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/henritersteeg.wordpress.com/100/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=henritersteeg.wordpress.com&amp;blog=6822465&amp;post=100&amp;subd=henritersteeg&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://henritersteeg.wordpress.com/2009/03/30/generic-db-caching-in-google-app-engine/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/ef0d9721eb3e847877e3f6f8f665620a?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">Henri</media:title>
		</media:content>
	</item>
		<item>
		<title>Performance tuning on Google App Engine</title>
		<link>http://henritersteeg.wordpress.com/2009/03/20/performance-tuning-on-google-app-engine/</link>
		<comments>http://henritersteeg.wordpress.com/2009/03/20/performance-tuning-on-google-app-engine/#comments</comments>
		<pubDate>Fri, 20 Mar 2009 08:37:09 +0000</pubDate>
		<dc:creator>Henri</dc:creator>
				<category><![CDATA[Google App Engine]]></category>
		<category><![CDATA[GAE]]></category>

		<guid isPermaLink="false">http://henritersteeg.wordpress.com/?p=75</guid>
		<description><![CDATA[After profiling my app (see Performance profiling on GAE), it&#8217;s time to do some actual tuning. The problem is, that GAE&#8217;s datastore is not very smart about entities already loaded in the same session or request. For this reason other ORM tools like (n)Hibernate use a Session Cache. Interacting with the datastore always goes through [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=henritersteeg.wordpress.com&amp;blog=6822465&amp;post=75&amp;subd=henritersteeg&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>After profiling my app (see <a href="http://henritersteeg.wordpress.com/2009/03/19/performance-profiling-on-google-app-engine/">Performance profiling on GAE</a>), it&#8217;s time to do some actual tuning. The problem is, that GAE&#8217;s datastore is not very smart about entities already loaded in the same session or request. For this reason other ORM tools like <a href="http://www.nhibernate.org">(n)</a><a href="http://www.hibernate.org">Hibernate</a> use a Session Cache. Interacting with the datastore always goes through a session cache in Hibernate. There is only a single instance of a retrieved entity in the session cache.</p>
<p><span id="more-75"></span><br />
Now this is quite different from the GAE datastore. As we saw in my previous post, every reference to an existing entity is</p>
<ul>
<li>fetched separately from the datastore, and</li>
<li>leads to a new instance in memory.</li>
</ul>
<h3>Using memcache</h3>
<p>Of course, we already have a caching mechanism in GAE: memcache. So, let&#8217;s use that and figure out if that will give the performance boost we need.</p>
<p>In order to prevent referenced entities to load more than once, we can subclass db.ReferenceProperty and use our own version:</p>
<p><pre class="brush: python;">
class CachedReferenceProperty(db.ReferenceProperty):

  def __init__(self,
               reference_class=None,
               time=0,
               verbose_name=None,
               collection_name=None,
               **attrs):
    super(CachedReferenceProperty, self).__init__(reference_class, verbose_name, collection_name, **attrs)
    self.time = time
</pre><br />
In the class definition above, a <b>time</b> parameter is added, which will be passed on to memcache. It determines the number of seconds an entity is cached. </p>
<p>In order to intervene with the actual database access, we need to override the <b>__get__</b> method. For convenience I have duplicated the original __get__ method in our class and patched it a bit:<br />
<pre class="brush: python;">
  def __get__(self, model_instance, model_class):
    if model_instance is None:
      return self
    if hasattr(model_instance, self.__id_attr_name()):
      reference_id = getattr(model_instance, self.__id_attr_name())
    else:
      reference_id = None
    if reference_id is not None:
      resolved = getattr(model_instance, self.__resolved_attr_name())
      if resolved is not None:
        return resolved
      else:
        reference_str = str(reference_id)
        instance = memcache.get(reference_str)
        if instance is None:
          instance = db.get(reference_id)
          memcache.set(reference_str, instance, self.time)
        else:
          logging.info(&quot;CachedReferenceProperty - found object in cache: &quot;+str(reference_id))
        if instance is None:
          raise Error('ReferenceProperty failed to be resolved')
        setattr(model_instance, self.__resolved_attr_name(), instance)
        return instance
    else:
      return None
</pre><br />
The changed part are <b>lines 13 through 19</b> in the code above. Originally, there was only <strong>line 16</strong> present.</p>
<p>Now, in order to get this code working, I had to duplicate the two methods below as well. Since I am a Python novice, I don&#8217;t understand why I had to do this, but it works&#8230; (if someone knows, please leave a comment <img src='http://s1.wp.com/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> )<br />
<pre class="brush: python;">
  def __id_attr_name(self):
    return self._attr_name()

  def __resolved_attr_name(self):
    return &quot;_RESOLVED&quot;+self._attr_name()
</pre></p>
<p>And now the results of actually applying this caching method. I used the CachedReferenceProperty with a 5 second timeout, which should be good enough to cache the entity during the request lifetime.<br />
<pre class="brush: python;">
    90565 function calls (88670 primitive calls) in 3.601 CPU seconds
   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
       61    1.858    0.030    1.921    0.031 {google3.apphosting.runtime._apphosting_runtime___python__apiproxy.Wait}
      523    0.674    0.001    0.674    0.001 {posix.stat}
       61    0.141    0.002    0.160    0.003 apiproxy.py:119(_MakeCallImpl)
</pre><br />
Without the caching, the results are (around the same time as the previous call):<br />
<pre class="brush: python;">
    81628 function calls (80775 primitive calls) in 2.840 CPU seconds
   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
       39    1.169    0.030    1.222    0.031 {google3.apphosting.runtime._apphosting_runtime___python__apiproxy.Wait}
      973    1.130    0.001    1.130    0.001 {posix.stat}
       37    0.034    0.001    1.214    0.033 traceback.py:273(extract_stack)
       39    0.027    0.001    0.039    0.001 apiproxy.py:119(_MakeCallImpl)
</pre><br />
So, the performance is actually <b>worse</b>! The response time increased from 2.8 seconds to 3.6 seconds using memcache. And the number of <code>apiproxy.Wait</code> calls increased from 39 to 61. Which is not strange, since memcache also uses remoting&#8230; And that, of course, is also the reason that this solution is actually worse.</p>
<p>The lesson to be learned here, is that memcache should be used on a more granular level, caching actions that involve a lot more than just getting an entity from the datastore by key&#8230;</p>
<h3>Using a dictionary</h3>
<p>Since we only want to cache entities during the duration of a request, the entities could just as well be stored locally in memory. So we change our CachedReferenceProperty class:<br />
<pre class="brush: python;">
class CachedReferenceProperty(db.ReferenceProperty):

  _cache = weakref.WeakValueDictionary({})
    
  def __get__(self, model_instance, model_class):
    ...
        instance = CachedReferenceProperty._cache.get(reference_id, None)
        if instance is None:
          instance = db.get(reference_id)
          CachedReferenceProperty._cache[reference_id] = instance
        else:
          logging.info(&quot;CachedReferenceProperty - found object in cache: &quot;+str(reference_id))
    ...
</pre><br />
We use a <code>WeakValueDictionary</code> so that we don&#8217;t actively have to clean up after our selves (that is: removing the entries from the cache dictionary after usage). Entries are removed as soon as there is no reference to the entity anymore. This will usually be at the end of the request. Here are the results of this approach:<br />
<pre class="brush: python;">
    66706 function calls (65996 primitive calls) in 1.578 CPU seconds
   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
      538    0.619    0.001    0.619    0.001 {posix.stat}
       23    0.543    0.024    0.581    0.025 {google3.apphosting.runtime._apphosting_runtime___python__apiproxy.Wait}
       23    0.028    0.001    0.036    0.002 apiproxy.py:119(_MakeCallImpl)
</pre></p>
<p>The number of <code>apiproxy.Wait</code> calls decreased from <b>39 to 23</b> and, better, the response time decreased from <b>2.8 to 1.6</b> seconds!</p>
<br />Posted in Google App Engine Tagged: GAE, Google App Engine <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/henritersteeg.wordpress.com/75/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/henritersteeg.wordpress.com/75/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/henritersteeg.wordpress.com/75/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/henritersteeg.wordpress.com/75/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/henritersteeg.wordpress.com/75/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/henritersteeg.wordpress.com/75/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/henritersteeg.wordpress.com/75/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/henritersteeg.wordpress.com/75/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/henritersteeg.wordpress.com/75/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/henritersteeg.wordpress.com/75/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/henritersteeg.wordpress.com/75/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/henritersteeg.wordpress.com/75/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/henritersteeg.wordpress.com/75/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/henritersteeg.wordpress.com/75/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=henritersteeg.wordpress.com&amp;blog=6822465&amp;post=75&amp;subd=henritersteeg&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://henritersteeg.wordpress.com/2009/03/20/performance-tuning-on-google-app-engine/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/ef0d9721eb3e847877e3f6f8f665620a?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">Henri</media:title>
		</media:content>
	</item>
		<item>
		<title>Performance profiling on Google App Engine</title>
		<link>http://henritersteeg.wordpress.com/2009/03/19/performance-profiling-on-google-app-engine/</link>
		<comments>http://henritersteeg.wordpress.com/2009/03/19/performance-profiling-on-google-app-engine/#comments</comments>
		<pubDate>Thu, 19 Mar 2009 12:10:18 +0000</pubDate>
		<dc:creator>Henri</dc:creator>
				<category><![CDATA[Google App Engine]]></category>
		<category><![CDATA[GAE]]></category>

		<guid isPermaLink="false">http://henritersteeg.wordpress.com/?p=19</guid>
		<description><![CDATA[My GAE application for table tennis players does not perform very well. Although some parts are exceptionally fast, getting the results of a players team matches, is quite slow. I can understand it&#8217;s slower than searching for a player, but over ten times as slow? No. Sorry. I don&#8217;t understand. Profiling, first attempt It&#8217;s time [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=henritersteeg.wordpress.com&amp;blog=6822465&amp;post=19&amp;subd=henritersteeg&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>My GAE application for table tennis players does not perform very well. Although some parts are exceptionally fast, getting the results of a players team matches, is quite slow. I can understand it&#8217;s slower than searching for a player, but <strong>over ten times as slow</strong>? No. Sorry. I don&#8217;t understand.</p>
<p><span id="more-19"></span></p>
<h3>Profiling, first attempt</h3>
<p>It&#8217;s time to find out what&#8217;s going on. So let&#8217;s turn profiling on! There&#8217;s a <a href="http://code.google.com/appengine/kb/commontasks.html#profiling">FAQ entry</a> on how to do that. After adding the code and running it on the development server, I was a bit puzzled. The majority of the time is spent on <code>eval()</code> functions. Digging through the output a bit, made me realize profiling should not be executed on the development server.</p>
<p><pre class="brush: python;">
    1873187 function calls (1871823 primitive calls) in 10.741 CPU seconds

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    15174    0.763    0.000    2.506    0.000 datastore_types.py:175(from_path)
     7587    0.675    0.000    4.605    0.001 {eval}
    15174    0.610    0.000    1.386    0.000 datastore_types.py:422(__repr__)
     7609    0.530    0.000    1.308    0.000 datastore_types.py:442(__cmp__)
</pre></p>
<p>It looks like querying the datastore is done by creating expressions using repr() and subsequent calls to <code>eval()</code> to check the query conditions&#8230;</p>
<h3>Profiling, 2nd attempt</h3>
<p>Running the same code on Google App Engine reveals totally different usage stats:</p>
<p><pre class="brush: python;">
    64751 function calls (63972 primitive calls) in 0.957 CPU seconds

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
       37    0.625    0.016    0.674    0.017 {google3.apphosting.runtime._apphosting_runtime___python__apiproxy.Wait}
        2    0.018    0.009    0.018    0.009 {method 'read' of 'file' objects}
       37    0.017    0.000    0.028    0.001 apiproxy.py:119(_MakeCallImpl)
</pre></p>
<p>As you can see above, time is mostly (<b>65%</b>) spent on a call to <code>apiproxy.Wait</code>. So, what is Google waiting for? I can guess, of course: datastore I/O. So, let&#8217;s find out if that is the case&#8230;</p>
<h3>DB Profiling</h3>
<p>For database profiling, I turned to an article: <a href="http://code.google.com/appengine/articles/hooks.html">Using hooks in GAE</a>. Using a &#8220;pre-call&#8221; hook, I log all <code>datastore_v3</code> calls:</p>
<p><pre class="brush: python;">
def patch_appengine():
    def hook(service, call, request, response):
        logging.info('%s %s - %s' % (service, call, str(request)))
        stack = traceback.format_stack()
        logging.debug('%s %s - %s' % (service, call, &quot;n&quot;.join(stack)))
      
    apiproxy_stub_map.apiproxy.GetPreCallHooks().Append('db_log', hook, 'datastore_v3')
</pre></p>
<p>It turns out that 36 out of the 37 <code>apiproxy.Wait</code> are accounted for in the log. These are, as we expected, datastore calls. But there is an interesting thing going on, looking a part of the log:</p>
<p><pre class="brush: python;">
3:39 30.425 Get - key .. {type: &quot;Team&quot; name: &quot;Shot 1&quot;}
3:39 30.506 Get - key .. {type: &quot;Team&quot; name: &quot;Bijmaat 3&quot;}
3:39 30.540 Get - key .. {type: &quot;Team&quot; name: &quot;Almeerspin 2&quot;}
3:39 30.580 Get - key .. {type: &quot;Team&quot; name: &quot;Shot 1&quot;}
3:39 30.614 Get - key .. {type: &quot;Team&quot; name: &quot;Maarssen 1&quot;}
3:39 30.651 Get - key .. {type: &quot;Team&quot; name: &quot;Shot 1&quot;}
3:39 30.678 Get - key .. {type: &quot;Team&quot; name: &quot;Shot 1&quot;}
3:39 30.702 Get - key .. {type: &quot;Team&quot; name: &quot;SVO 2&quot;}
3:39 30.732 Get - key .. {type: &quot;Team&quot; name: &quot;Shot 1&quot;}
3:39 30.755 Get - key .. {type: &quot;Team&quot; name: &quot;Laren 1&quot;}
</pre><br />
Now, let&#8217;s get a little bit of background: I am profiling the part of my app that is showing the matches of my team (&#8220;Shot 1&#8243;). Every match references two teams using <code>db.ReferenceProperty(Team)</code>. The log output above is the result of showing the first 5 matches. </p>
<p>As you can see above, <b>my team is retrieved from the datastore for every single match</b>, using an expensive (16 ms on average) remote procedure call!</p>
<p>Optimizing this is something I will work on in my next post.</p>
<br />Posted in Google App Engine Tagged: GAE, Google App Engine <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/henritersteeg.wordpress.com/19/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/henritersteeg.wordpress.com/19/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/henritersteeg.wordpress.com/19/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/henritersteeg.wordpress.com/19/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/henritersteeg.wordpress.com/19/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/henritersteeg.wordpress.com/19/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/henritersteeg.wordpress.com/19/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/henritersteeg.wordpress.com/19/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/henritersteeg.wordpress.com/19/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/henritersteeg.wordpress.com/19/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/henritersteeg.wordpress.com/19/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/henritersteeg.wordpress.com/19/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/henritersteeg.wordpress.com/19/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/henritersteeg.wordpress.com/19/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=henritersteeg.wordpress.com&amp;blog=6822465&amp;post=19&amp;subd=henritersteeg&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://henritersteeg.wordpress.com/2009/03/19/performance-profiling-on-google-app-engine/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/ef0d9721eb3e847877e3f6f8f665620a?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">Henri</media:title>
		</media:content>
	</item>
		<item>
		<title>User Interface widgets: from YUI to jQuery UI</title>
		<link>http://henritersteeg.wordpress.com/2009/03/11/user-interface-widgets-from-yui-to-jquery-ui/</link>
		<comments>http://henritersteeg.wordpress.com/2009/03/11/user-interface-widgets-from-yui-to-jquery-ui/#comments</comments>
		<pubDate>Wed, 11 Mar 2009 13:19:04 +0000</pubDate>
		<dc:creator>Henri</dc:creator>
				<category><![CDATA[Javascript libraries]]></category>
		<category><![CDATA[jQuery UI]]></category>
		<category><![CDATA[YUI]]></category>

		<guid isPermaLink="false">http://henritersteeg.wordpress.com/?p=10</guid>
		<description><![CDATA[At the moment I am developing a a table tennis community site, complete with match statistics. It will be totally player centered. Hence the first thing you do on the site is searching for a player. After selecting a player you get all the information on the matches of the current season, as well as [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=henritersteeg.wordpress.com&amp;blog=6822465&amp;post=10&amp;subd=henritersteeg&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>At the moment I am developing a a table tennis community site, complete with match statistics. It will be totally player centered. Hence the first thing you do on the site is searching for a player. After selecting a player you get all the information on the matches of the current season, as well as the historical ratings.</p>
<p><span id="more-10"></span></p>
<h3>Data</h3>
<p>Besides this &#8220;official&#8221; data, I want users to be able to add there own content:</p>
<ul>
<li>reviews on tournaments they played / viewed,</li>
<li>discussions on material (rubbers, frames, balls, tables etc.),</li>
<li>general discussion forum.</li>
</ul>
<p>This is a lot of data, which somehow needs to be organized on the screen.</p>
<h3>Yahoo User Interface (YUI)</h3>
<p>This is a research project, so I decided to try out a ready made Javascript and CSS library. I was drawn to the Yahoo CSS library, since it seems a solid solution for cross browser compatibility. I took on the javascript library as well: I also needed tabs, dialogs and data tables.</p>
<p>The initial experience with YUI was very good. A lot of documentation and examples are provided. However, as soon as I created something more complex, unexpected results would occur. Usually leading to: &#8220;Where did my tabs/datatable/dialog/etc. go?!?&#8221;. The required markup is quite strict. The javascript library is workable, but a bit tedious. Lot&#8217;s of components needed to throw together a workable component.</p>
<p>And to make things worse:  there <strong>will </strong>be a lot of differences between Firefox and IE. After working with it for 3-4 weeks, I gave up: Too much trouble and not enough fun for what I want to accomplish&#8230;</p>
<h3>jQuery UI</h3>
<p>At first I declined jQuery (UI) because there was no dataTable component&#8230;. I thought. I totally missed the plugins, since I assumed that would be solutions for some edge cases. Not the things you need when you first start using jQuery. I couldn&#8217;t be more wrong. The list of plugins is endless and most of them are maintained actively. There is a plugin for almost every problem you can think of&#8230;</p>
<p>And then I ran into the themable UI part. Since I eventually want the user interface to be customized by the end user, I was sold instantly. I can now offer the user a selection of over 10 themes to pick from, without any effort. And they all look very polished. Way to go!</p>
<p>It took me almost a week to migrate the application from YUI to jQuery, learning jQuery syntax on the fly. And that&#8217;s not hard at all. Very intuitive syntax, simple solutions and sensible defaults. That&#8217;s what I like! So, a big thank you to the jQuery team from me.</p>
<p>Programming in javascript just became fun!</p>
<br />Posted in Javascript libraries Tagged: jQuery UI, YUI <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/henritersteeg.wordpress.com/10/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/henritersteeg.wordpress.com/10/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/henritersteeg.wordpress.com/10/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/henritersteeg.wordpress.com/10/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/henritersteeg.wordpress.com/10/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/henritersteeg.wordpress.com/10/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/henritersteeg.wordpress.com/10/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/henritersteeg.wordpress.com/10/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/henritersteeg.wordpress.com/10/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/henritersteeg.wordpress.com/10/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/henritersteeg.wordpress.com/10/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/henritersteeg.wordpress.com/10/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/henritersteeg.wordpress.com/10/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/henritersteeg.wordpress.com/10/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=henritersteeg.wordpress.com&amp;blog=6822465&amp;post=10&amp;subd=henritersteeg&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://henritersteeg.wordpress.com/2009/03/11/user-interface-widgets-from-yui-to-jquery-ui/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/ef0d9721eb3e847877e3f6f8f665620a?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">Henri</media:title>
		</media:content>
	</item>
		<item>
		<title>Google App Engine, IE7 and HTTP/1.0 issues</title>
		<link>http://henritersteeg.wordpress.com/2009/03/04/google-app-engine-ie7-and-http10-issues/</link>
		<comments>http://henritersteeg.wordpress.com/2009/03/04/google-app-engine-ie7-and-http10-issues/#comments</comments>
		<pubDate>Wed, 04 Mar 2009 11:48:30 +0000</pubDate>
		<dc:creator>Henri</dc:creator>
				<category><![CDATA[Google App Engine]]></category>
		<category><![CDATA[GAE]]></category>

		<guid isPermaLink="false">http://henritersteeg.wordpress.com/?p=3</guid>
		<description><![CDATA[Lately I have been developing a web application using Google App Engine. During development I use Firefox extensively to check the layout of the application. Every once in a while I also check the site under IE7. Yesterday I ran into some strange problems. I use jQuery to issue ajax calls, to update parts of [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=henritersteeg.wordpress.com&amp;blog=6822465&amp;post=3&amp;subd=henritersteeg&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Lately I have been developing a web application using Google App Engine. During development I use Firefox extensively to check the layout of the application. Every once in a while I also check the site under IE7.</p>
<p>Yesterday I ran into some strange problems. I use jQuery to issue ajax calls, to update parts of the screen, but under IE7 a DIV did not seem to be updated. After checking the development web server, I noticed that the Ajax call was not sent. Since I am pretty sure the Ajax call was issued, something else was going on.</p>
<p><span id="more-3"></span></p>
<h3>Caching</h3>
<p>The most probable cause was, that IE7 is caching the contents. Since I did not explicitly set caching options on the response, I checked this using Firebug. However, Firebug doesn&#8217;t show this. So I turned to &#8220;curl&#8221; using the verbose option. That clearly showed:</p>
<blockquote>
<pre> Cache-Control: no-cache</pre>
</blockquote>
<p>And that&#8217;s exactly what I want! So, why does it not work? I did notice the development web server responded with HTTP/1.0. After &#8220;googling&#8221; around a bit, it turns out that the Cache-Control header was added in HTTP/1.1. IE7 was just very strict in applying the standards (that&#8217;s a change!).</p>
<h3>Upgrading the development server</h3>
<p>In trying to get this working on the Google App Engine development web server, I digged into the code of the DevAppServerRequestHandler class and added:</p>
<blockquote>
<pre>protocol_version = 'HTTP/1.1'</pre>
</blockquote>
<p>This turned out to be a bad idea. IE7 now got stuck when the page was loaded. According to the dev. web server the response was sent, but IE7 was still waiting for more. So, I reverted that change and tested it by deploying a new version to the Google App Engine, since this does support the HTTP/1.1 protocol. Now everything worked fine in IE7, the Ajax response was not cached and reloaded every single time&#8230;</p>
<br />Posted in Google App Engine Tagged: GAE, Google App Engine <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/henritersteeg.wordpress.com/3/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/henritersteeg.wordpress.com/3/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/henritersteeg.wordpress.com/3/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/henritersteeg.wordpress.com/3/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/henritersteeg.wordpress.com/3/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/henritersteeg.wordpress.com/3/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/henritersteeg.wordpress.com/3/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/henritersteeg.wordpress.com/3/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/henritersteeg.wordpress.com/3/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/henritersteeg.wordpress.com/3/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/henritersteeg.wordpress.com/3/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/henritersteeg.wordpress.com/3/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/henritersteeg.wordpress.com/3/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/henritersteeg.wordpress.com/3/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=henritersteeg.wordpress.com&amp;blog=6822465&amp;post=3&amp;subd=henritersteeg&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://henritersteeg.wordpress.com/2009/03/04/google-app-engine-ie7-and-http10-issues/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/ef0d9721eb3e847877e3f6f8f665620a?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">Henri</media:title>
		</media:content>
	</item>
	</channel>
</rss>
