15

我为博客/新闻网站编写代码。主页有 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() 不是一个好主意。无论如何,我需要知道这种行为的确切原因以及如何处理它。

4

1 回答 1

24

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

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

祝你好运!

于 2012-06-16T15:17:23.543 回答