首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么没有从NDB的上下文缓存中获取一个实体?

为什么没有从NDB的上下文缓存中获取一个实体?
EN

Stack Overflow用户
提问于 2013-02-13 18:55:55
回答 1查看 628关注 0票数 4

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

归结起来是这样的:

代码语言:javascript
复制
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()不止一次将非常快,因为第一个调用很可能从memcache检索Settings实体(因为该实体很少更新),并且在同一个请求处理程序中的所有后续调用都将从NDB的上下文缓存中检索它。来自文档

上下文缓存仅在单个传入HTTP请求的持续时间内保持,并且仅对处理该请求的代码“可见”。它很快;这个缓存存在于内存中。

但是,AppStat显示我的Settings实体在同一个请求处理程序中被多次从memcache中检索。通过查看AppStat中请求处理程序的详细页面,展开对memcache.Get的每个调用的调用跟踪,并查看正在重新处理的memcahe键,我就知道了这一点。

我在请求处理程序中使用了大量的微线程,我从需要访问设置的微线程中调用Settings.singleton()。这是否是为什么再次从memcache而不是从上下文缓存中获取Settings实体的原因?如果是这样的话,控制是否可以从上下文缓存中获取实体的确切规则是什么?我未能在NDB文档中找到这一信息。

更新2013/02/15:我无法在虚拟测试应用程序中再现这一特性。测试代码是:

代码语言:javascript
复制
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()

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

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

更新2013/02/21:我终于能够在一个虚拟测试应用程序中再现这一结果。测试代码是:

代码语言:javascript
复制
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调用了一个topleveltoplevel似乎创建了一个新的ndb上下文。

将嵌套的toplevel更改为synctasklet解决了这个问题。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2013-08-28 19:58:46

这似乎是一种新的ndb上下文。

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

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

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/14860901

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档