0

我正在尝试显示一个包含约 800 个实体的表格,并且在阻止它变得非常慢时遇到了问题。(就像慢了 15-20 秒。)我成功实现了 memcache,但是因为我为每个子实体引用了一个父模型,它仍然会导致每个 800 的 datastore_v3.Get 并且非常慢。

然后我实现了 Nick Johnson 的ReferenceProperty 预取,但无法解决以下错误:

[... snipped ...]
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/lib/webapp2/webapp2.py", line 570, in dispatch
  return method(*args, **kwargs)
File "/myurl/mypythoncode.py", line 67, in get
  prefetch_refprops(entitylist, ChildModel.parent_program.name)
File "/myurl/mypythoncode.py", line 36, in prefetch_refprops
  fields = [(entity, prop) for entity in entities for prop in props]
TypeError: 'NoneType' object is not iterable

楷模:

这是两个相关的模型:

class ParentModel(db.Model):
  name = db.StringProperty()
  # currently 109 of these

class ChildModel(db.Model):
  name = db.StringProperty()
  parent_program = db.ReferenceProperty(ParentModel)
  website = db.StringProperty()
  # currently 758 of these

Python代码:

在我的 Python 代码中,我使用了 Nick Johnson 的高效模型缓存ReferenceProperty 预取技术。(我在下面包含了 ReferenceProperty 预取,但没有包含 memcaching 代码。)

class AllEntities(webapp2.RequestHandler):
  def get(self):
    entitylist = deserialize_entities(memcache.get("entitylist"))
    entityref = prefetch_refprops(entitylist, ChildModel.parent_program.name)
    if not entitylist:
      entitylist = ChildModel.all().fetch(None)
      entityref = prefetch_refprops(entitylist, ChildModel.parent_program.name)
      memcache.set("entitylist", serialize_entities(entitylist))
    context = {
      'entitylist': entitylist,
    }
    self.response.out.write(template.render(context))

def prefetch_refprops(entities, *props):
    fields = [(entity, prop) for entity in 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):
        prop.__set__(entity, ref_entities[ref_key])
    return entities

Jinja2 模板:

我的 Jinja2 模板引用了“entitylist”中的可迭代“条目”,还引用了 parent_program.name 和 parent_program.key().id()

{% for entry in entitylist %}
  <tr>
    <td><a href="{{ entry.website}}">{{ entry.website }}</a></td>
    <td><a href="/urlcategory/view?entityid={{ entry.parent_program.key().id() }}">{{ entry.parent_program.name }}</td>
  </tr>
{% endfor %}

我已经更换了这条线:

entityref = prefetch_refprops(entitylist, ChildModel.parent_program.name)

entityref = prefetch_refprops(entitylist, ChildModel.parent_program)

以及包括“.name”和“.key().id()”在内的其他变体。当我使用“.key().id()”时出现错误:

AttributeError: 'ReferenceProperty' object has no attribute 'key'

我错过了什么或搞砸了什么?我真的很感激任何帮助!

4

1 回答 1

1

杰德,你做对了:)

两个改进:

  1. 您不需要分配预取的返回值,因为它没有被使用,并且公司列表将被修改到位。
  2. 我使用 prefetch_refprops 的略微修改版本来处理未填充引用属性的情况。

    def prefetch_refprops(entities, *props):
        fields = [(entity, prop) for entity in entities for prop in props]
        ref_keys_all = [prop.get_value_for_datastore(x) for x, prop in fields]
        ref_keys = [ref_key for ref_key in ref_keys_all if ref_key is not None]
        ref_entities = dict((x.key(), x) for x in db.get(set(ref_keys)))
        for (entity, prop), ref_key in zip(fields, ref_keys_all):
            if ref_key and ref_entities[ref_key]:
                prop.__set__(entity, ref_entities[ref_key])
            else:
                prop.__set__(entity, None)
        return entities
    

我们在生产代码中使用它,它产生了真正的不同。下面是一个在构建模板值的代码上打开/关闭预取的示例。

(run:   real_time, Get #rpcs, RunQuery #rpcs)
Before:   5044 ms,       132,    101
After:    2214 ms,        53,     11

我们的代码正在执行的另一个重链梯操作是每个对象的 ref_set 上的 count(),我们将在不久的将来将其替换为缓存对象上的值。

于 2012-10-12T23:19:28.747 回答