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

时间:2012-06-16 12:50:58

标签: python google-app-engine google-cloud-datastore

我为博客/新闻网站编写代码。主页有10篇最新文章,还有一个存档部分,所有文章按修改时间降序排序。在存档部分中,我使用基于游标的分页,并且我从第二页开始缓存结果,因为只有在发布新文章或由于某种原因现有的文章发布时才更改页面。每页有10篇文章。因此,当用户点击具有某个数字(而不是第一个)的存档页面时,将首先检查该页面编号的内存缓存。如果页面不存在,则检查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()函数,它按预期工作。我把()更新到数据存储区之后以及在cacheArchivePages()函数中获取ArticlesPage()之前,是否已经过了很少的时间?也许写交易还没有完成,所以我得到了旧结果?我尝试使用time.sleep()并在调用cacheArchivePages()之前等待几秒钟,在这种情况下我无法重现该行为,但在我看来time.sleep()并不是一个好主意。无论如何,我需要知道这种行为的确切原因以及如何处理它。

1 个答案:

答案 0 :(得分:23)

你最有可能被“最终一致的查询”所击中。使用HR数据存储区时,查询可能会使用稍微过时的数据,并且put()写入的数据需要一段时间才能对查询可见(按键或id没有get()的延迟)。延迟通常以秒为单位进行测量,但我认为我们不保证上限 - 如果你被一个不幸的网络分区击中,我想可能是几个小时。

有各种各样的部分解决方案,从最近一次写入的作者查看查询结果到使用祖先查询(具有各自的限制)时作弊。您可能只是为缓存提供有限的生命周期,并在读取时更新它而不是写入。

祝你好运!