2012-07-14 51 views
0

我試圖展示一個約800個實體的表格,並且遇到了難以保持真正緩慢的問題。 (慢15-20秒)。我成功實現了memcache,但是因爲我爲每個子實體引用了一個父模型,它仍然會導致每個800的datastore_v3.Get,並且速度很慢。如何通過預取ReferenceProperty屬性來防止過多的RPC調用?

我再實施尼克·約翰遜的ReferenceProperty prefetching並不能解決以下錯誤:

[... 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的技術efficient model memcachingReferenceProperty prefetching。 (我已經包括下面的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

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

{% 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 %} 

我已經取代了線。名稱和parent_program.key()ID()

entityref = prefetch_refprops(entitylist, ChildModel.parent_program) 

和包括 「名稱」 和其它變型 「.KEY()。ID()」。當我使用「.key()。id()」我得到的錯誤:

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

我在想什麼或搞砸了?我非常感謝任何幫助!

+0

刪除第一個'entityref = prefetch_refprops(entitylist,ChildModel.parent_program.name)'行。 'entitylist'在第一次通過時被設置爲None,所以預取失敗。 – mjibson 2012-07-15 02:13:27

+0

謝謝,我解決了這個問題。但是現在我得到了「prefetch_refprops(companylist,ChildModel.parent_program.name)」的「AttributeError:'str'對象沒有屬性'get_value_for_datastore'」。這看起來很奇怪,因爲它實際上與Nick在他的例子中使用的完全相同。 – 2012-07-15 09:42:00

+0

好吧,我現在修改它,將其更改爲「prefetch_refprops(companylist,ChildModel.parent_program)」。我沒有得到任何更多的錯誤,但它感覺我仍然沒有做正確的事情...... – 2012-07-15 11:03:56

回答

1

傑德,你這樣做是正確的:)

兩個方面的改進:

  1. 你並不需要分配預取的返回值不被使用和companylist它將被修改到位。
  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每個對象,我們將在不久的將來與對象緩存值替換的計數()。

+0

謝謝,Campey。我最終用一種不同的方法,由Guido在這裏概述:http://stackoverflow.com/questions/9127982/avoiding-memcache-1m-limit-of-values/9143912#9143912但它絕對有效! – 2012-10-21 14:45:50