Monday, April 28, 2008

Tips on writing scalable apps

Google App Engine makes writing scalable apps easier, and a well-designed app should be able to grow automatically from one user to millions. What does it mean for a Google App Engine application to be "well-designed," though? Here are a few simple tips that, if you design with them in mind, should help ensure your App will stand up with grace and aplomb, even under heavy load.
  • Avoid contention on datastore entities. If every request to your app reads or writes a particular entity, latency will increase as your traffic goes up because reads and writes on a given entity are sequential. One example construct you should avoid at all costs is the gobal counter, i.e. an entity that keeps track of a count and is updated or read on every request. There are some interesting ways of simulating this behavior that don't require reads/writes on every request, and we'll talk about a handy way to cache entities for reads below.
  • Avoid large entity groups. Any two entities that share a common ancestor belong to the same entity groups. All writes to an entity group are sequential, so large entity groups can bog down popular apps quickly if there are a lot of writes to that group. Instead, use small, localized groups in your design.
  • Write sparingly. Writes are more expensive than reads; keep this in mind when designing your data model. If you can avoid a write--it's best to do so.
  • Define a main() function for code reuse. Instances of your app are kept running for a certain period after each request, so there's a chance that any request will hit an already-running app. Apps that define main as in the following example:

    def main():
       application = webapp.WSGIApplication(_URLS, debug=True)

    if __name__ == '__main__':
    can reuse global variables, so although there's no guarantee that any given request will hit a running app instance, you can take advantage of this to non-deterministically cache entities and other expensive values. This works simply because App Engine will detect if main() is present in an already-loaded script and call it instead of re-importing.
  • Profile your code. You can use the Python profiler to profile your code's performance.
Do you have tips for scaling web apps on App Engine? Post them in our Google Group!