通过聚合RPC调用来加速GAE-Py中的模板

时间:2010-01-16 06:52:31

标签: python google-app-engine django-templates

这是我的问题:

class City(Model):
  name = StringProperty()

class Author(Model):
  name = StringProperty()
  city = ReferenceProperty(City)

class Post(Model):
  author = ReferenceProperty(Author)
  content = StringProperty()

代码并不重要......它的django模板:

{% for post in posts %}
<div>{{post.content}}</div>
<div>by {{post.author.name}} from {{post.author.city.name}}</div>
{% endfor %}

现在假设我使用Post.all().fetch(limit=100)获得前100个帖子,并将此列表传递给模板 - 会发生什么?

它使得 200更多数据存储区得到 - 每个作者获得100个,获得每个作者城市100个。

实际上,这是完全可以理解的,因为帖子只提到了作者,而作者只提到了这个城市。 __get__post.author对象上的author.city访问者透明地获取并提取数据(请参阅this问题)。

围绕这个的一些方法是

  1. 使用Post.author.get_value_for_datastore(post)收集作者密钥(请参阅上面的链接),然后批量获取以获取所有密钥 - 这里的麻烦是我们需要重新构建模板数据对象...需要为每个模型和处理程序提供额外代码和维护的东西。
  2. 编写一个访问器,比如cached_author,首先检查作者的memcache并返回 - 这里的问题是post.cached_author将被调用100次,这可能意味着100个memcache调用。
  3. 如果数据不一定是最新的,请将静态键保存到对象图(并在五分钟内刷新一次)。然后,cached_author访问者可以参考此地图。
  4. 所有这些想法都需要额外的代码和维护,而且它们不是很透明。如果我们能做什么

    @prefetch
    def render_template(path, data)    
      template.render(path, data)
    

    原来我们可以...... hooksGuido's instrumentation module都证明了这一点。如果@prefetch方法通过捕获请求的键来包装模板渲染,我们可以(至少一个深度级别)捕获正在请求的键,返回模拟对象,并批量访问它们。这可以针对所有深度级别重复,直到没有请求新密钥。最终渲染可以拦截获取并从地图返回对象。

    这会将 200 的总数变为 3 ,透明且无需任何额外代码。更不用说在无法使用memcache的情况下大大减少了对memcache的需求和帮助。

    麻烦是我不知道怎么做(还)。在我开始尝试之前,还有其他人这样做过吗?或者有人想帮忙吗?或者您是否看到计划中存在严重缺陷?

2 个答案:

答案 0 :(得分:1)

我一直处于类似情况。我没有ReferenceProperty,而是有父/子关系,但基础是相同的。我当前的解决方案并没有得到完善,但至少它足以用于报告和200-1,000个实体的事物,每个实体都有几个需要提取的子实体。

您可以批量手动搜索数据并根据需要进行设置。

# Given the posts, fetches all the data the template will need
# with just 2 key-only loads from the datastore.
posts = get_the_posts()

author_keys = [Post.author.get_value_for_datastore(x) for x in posts]
authors = db.get(author_keys)

city_keys = [Author.city.get_value_for_datastore(x) for x in authors]
cities = db.get(city_keys)

for post, author, city in zip(posts, authors, cities):
  post.author = author
  author.city = city

现在,当您渲染模板时,不会进行任何其他查询或提取。它的边缘很粗糙,但如果没有我刚才描述的这种模式,我就无法生存。

此外,您可能会考虑验证您的实体都不是None,因为如果密钥错误,db.get()将返回None。这只是进入基本数据验证。同样,如果存在超时等,则需要重试db.get()。

(最后,我认为memcache不会作为主要解决方案。可能作为加速数据存储调用的辅助层,但如果memcache为空,则需要运行良好。此外,Memcache本身有几个配额,如memcache调用和传输的总数据。过度使用memcache是​​一种很好的方法来杀死你的应用程序。)

答案 1 :(得分:0)