put() 后 App Engine 数据存储区中的读取延迟

Posted

技术标签:

【中文标题】put() 后 App Engine 数据存储区中的读取延迟【英文标题】:Read delay in App Engine Datastore after put() 【发布时间】:2012-06-19 06:52:33 【问题描述】:

我为博客/新闻网站编写代码。主页有 10 篇最新文章,还有一个存档部分,所有文章按修改时间降序排列。在存档部分,我使用基于游标的分页,并从第二页开始缓存结果,因为只有在新文章发布或现有文章出于某种原因进入草稿时才会更改页面。每页有10篇文章。因此,当用户点击带有某个编号(不是第一个编号)的存档页面时,首先会检查 memcache 是否有该页码结果。如果页面不存在,则检查 memcache 是否有该页面的游标,然后使用该游标从数据存储中获取结果:

class archivePage:
    def GET(self, page):
        if not page:
            articles = memcache.get('archivePage')
            if not articles:
                articles = fetchArticles()
                memcache.set('archivePage', articles)
        else:
            if int(page) == 0 or int(page) == 1:
                raise web.seeother('/archive')
            articles = memcache.get('archivePage'+page)
            if not articles:
                pageCursor = memcache.get('ArchivePageMapping'+page)
                if not pageCursor:
                    pageMapping = ArchivePageMapping.query(ArchivePageMapping.page == int(page)).get()
                    pageCursor = pageMapping.cursor
                    memcache.set('ArchivePageMapping'+page, pageCursor)
                articles = fetchArticles(cursor=Cursor(urlsafe=pageCursor))
                memcache.set('archivePage'+page, articles)

每次创建新文章或更改现有文章的状态(草稿/已发布)时,我都会刷新存档页面结果和游标的缓存。我在将文章保存到数据存储区后执行此操作:

class addArticlePage:     
    def POST(self):
        formData = web.input()
        if formData.title and formData.content:
            article = Article(title=formData.title,
                              content=formData.content,
                              status=int(formData.status))
            key = article.put()
            if int(formData.status) == 1:
                cacheArchivePages()
            raise web.seeother('/article/%s' % key.id())

def cacheArchivePages():
    articles, cursor, moreArticles = fetchArticlesPage()
    memcache.set('archivePage', articles)
    pageNumber=2
    while moreArticles:
        pageMapping = ArchivePageMapping.query(ArchivePageMapping.page == pageNumber).get()
        if pageMapping:
            pageMapping.cursor = cursor.urlsafe()
        else:
            pageMapping = ArchivePageMapping(page=pageNumber,
                                            cursor=cursor.urlsafe())
        pageMapping.put()
        memcache.set('ArchivePageMapping'+str(pageNumber), cursor.urlsafe())
        articles, cursor, moreArticles = fetchArticlesPage(cursor=cursor)
        memcache.set('archivePage'+str(pageNumber), articles)
        pageNumber+=1

问题来了。有时(没有规律,它是随机发生的)在刷新缓存后,我会得到与刷新前相同的存档页面结果和游标。例如,我添加了一篇新文章。它保存在数据存储中,并出现在档案的首页和第一页上(档案的第一页没有被缓存)。但是其他存档页面没有更新。我已经测试了我的 cacheArchivePages() 函数,它按预期工作。在我 put() 更新数据存储之后和我在 cacheArchivePages() 函数中 fetchArticlesPage() 之前,可能是因为时间太短了吗?也许写事务还没有完成,所以我得到了旧的结果?我尝试使用 time.sleep() 并在调用 cacheArchivePages() 之前等待几秒钟,在这种情况下我无法重现该行为,但在我看来 time.sleep() 不是一个好主意。无论如何,我需要知道这种行为的确切原因以及如何处理它。

【问题讨论】:

【参考方案1】:

您很可能会遇到“最终一致的查询”。使用 HR 数据存储时,查询可能会使用稍微旧的数据,并且 put() 写入的数据需要一段时间才能对查询可见(get() 通过 key 或 id 没有这种延迟)。延迟通常以秒为单位,但我认为我们不能保证有上限——如果您遇到不幸的网络分区,可能需要几个小时,我想。

有各种各样的部分解决方案,从最近写作的作者查看查询结果时作弊到使用祖先查询(它们有自己的局限性)。你可以简单地给你的缓存一个有限的生命周期,并在读取而不是写入时更新它。

祝你好运!

【讨论】:

谢谢贵多!我应该更加注意数据的一致性,并牢记 HR 数据存储的特​​点。在这种特殊情况下,我很容易比较旧结果和新结果,如果它们相同,则重新启动查询。 我投了赞成票,因为“使用祖先查询”正是解决我的应用程序延迟问题的方法,谢谢 Guido。

以上是关于put() 后 App Engine 数据存储区中的读取延迟的主要内容,如果未能解决你的问题,请参考以下文章

Google App Engine 数据存储区中不精确查询的良好模式是啥?

将来自 Android 应用程序的数据存储在 App Engine 数据存储区中

在 Google App Engine 上的数据存储区中更新大量实体

计算在 Google App Engine 数据存储区中计算分数和日期的索引

在“编译”时不知道属性名称的情况下,在 Python 中复制 Google App Engine 数据存储区中的实体

Google App Engine 数据存储区中每秒写入 5 次以上的事务计数器