google-app-engine - 为什么不从 NDB 的上下文缓存中获取实体?

标签 google-app-engine app-engine-ndb

我有一个用于存储一些全局应用程序设置的实体。这些设置可以通过管理 HTML 页面进行编辑,但很少更改。我只有这个实体的一个实例(某种单例),并且在我需要访问设置时总是引用这个实例。

归结为:

class Settings(ndb.Model):
    SINGLETON_DATASTORE_KEY = 'SINGLETON'

    @classmethod
    def singleton(cls):
        return cls.get_or_insert(cls.SINGLETON_DATASTORE_KEY)

    foo = ndb.IntegerProperty(
        default =  100,
        verbose_name = "Some setting called 'foo'",
        indexed = False)

@ndb.tasklet
def foo():
    # Even though settings has already been fetched from memcache and
    # should be available in NDB's in-context cache, the following call
    # fetches it from memcache anyways. Why?
    settings = Settings.singleton()

class SomeHandler(webapp2.RequestHandler):
    @ndb.toplevel
    def get(self):
        settings = Settings.singleton()
        # Do some stuff
        yield foo()
        self.response.write("The 'foo' setting value is %d" % settings.foo)

我假设每个请求处理程序多次调用 Settings.singleton() 会非常快,因为第一次调用很可能会检索 Settings来自内存缓存的实体(因为实体很少更新)并且同一请求处理程序中的所有后续调用将从 NDB 的上下文缓存中检索它。来自documentation :

The in-context cache persists only for the duration of a single incoming HTTP request and is "visible" only to the code that handles that request. It's fast; this cache lives in memory.

但是,AppStat 显示我的 Settings 实体在同一请求处理程序中多次从内存缓存中检索。我通过查看 AppStat 中请求处理程序的详细页面、扩展对 memcache.Get 的每次调用的调用跟踪并查看正在接收的 memcahe 键来了解这一点。

我在我的请求处理程序中使用了很多 tasklet,我从需要访问设置的 tasklet 中调用 Settings.singleton()。这可能是设置实体再次从内存缓存而不是上下文缓存中获取的原因吗?如果是这样,控制是否/何时可以从上下文缓存中获取实体的确切规则是什么?我无法在 NDB 文档中找到此信息。


2013/02/15 更新:我无法在虚拟测试应用程序中重现此内容。测试代码为:

class Foo(ndb.Model):
    prop_a = ndb.DateTimeProperty(auto_now_add = True)

def use_foo():
    foo = Foo.get_or_insert('singleton')
    logging.info("Function using foo: %r", foo.prop_a)

@ndb.tasklet
def use_foo_tasklet():
    foo = Foo.get_or_insert('singleton')
    logging.info("Function using foo: %r", foo.prop_a)

@ndb.tasklet
def use_foo_async_tasklet():
    foo = yield Foo.get_or_insert_async('singleton')
    logging.info("Function using foo: %r", foo.prop_a)

class FuncGetOrInsertHandler(webapp2.RequestHandler):
    def get(self):
        for i in xrange(10):
            logging.info("Iteration %d", i)
            use_foo()

class TaskletGetOrInsertHandler(webapp2.RequestHandler):
    @ndb.toplevel
    def get(self):
        logging.info("Toplevel")
        use_foo()
        for i in xrange(10):
            logging.info("Iteration %d", i)
            use_foo_tasklet()

class AsyncTaskletGetOrInsertHandler(webapp2.RequestHandler):
    @ndb.toplevel
    def get(self):
        logging.info("Toplevel")
        use_foo()
        for i in xrange(10):
            logging.info("Iteration %d", i)
            use_foo_async_tasklet()

在运行任何测试处理程序之前,我确保具有键名 singletonFoo 实体存在。

与我在生产应用程序中看到的相反,所有这些请求处理程序都在 Appstats 中显示对 memcache.Get 的单个调用。


2013 年 2 月 21 日更新: 我终于能够在虚拟测试应用程序中重现这一点。测试代码为:

class ToplevelAsyncTaskletGetOrInsertHandler(webapp2.RequestHandler):
    @ndb.toplevel
    def get(self):
        logging.info("Toplevel 1")
        use_foo()
        self._toplevel2()

    @ndb.toplevel
    def _toplevel2(self):
        logging.info("Toplevel 2")
        use_foo()
        for i in xrange(10):
            logging.info("Iteration %d", i)
            use_foo_async_tasklet()

此处理程序确实在 Appstats 中显示了对 memcache.Get 的 2 次调用,就像我的生产代码一样。

确实,在我的生产请求处理程序代码路径中,我有一个 toplevel 被另一个 toplevel 调用。似乎 toplevel 创建了一个新的 ndb 上下文。

将嵌套的 toplevel 更改为 synctasklet 可以解决问题。

最佳答案

It seems like a toplevel creates a new ndb context.

确切地说,每个带有 toplevel 装饰器的处理程序都有自己的上下文,因此有一个单独的缓存。您可以在下面的链接中查看 toplevel 的代码,在函数文档中指出 toplevel 是“设置新默认上下文的同步 tasklet”。

https://code.google.com/p/googleappengine/source/browse/trunk/python/google/appengine/ext/ndb/tasklets.py#1033

关于google-app-engine - 为什么不从 NDB 的上下文缓存中获取实体?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14860901/

相关文章:

google-app-engine - 如何确保与非祖先查询的隔离

python - NDB 按 StructuredProperty 的属性排序

google-app-engine - 在 Google 应用引擎上更改模型的后果

python - 使用 endpoint-proto-datastore 时设置父键而不是子键

python - 具有多个嵌套函数的 ndb.Tasklet 和 SyncTasklet

java - Java Google App Engine 中的多对多对象关系

google-app-engine - 在 Google 日历中哪里可以找到 eventId?

python-3.x - 我们如何在本地使用带有ndb的python3运行谷歌应用引擎

python - 如何通过 ndb 对 'Like' 机制进行建模?

python - Cloud Endpoints-Python 的自定义身份验证(用户模型)