我为博客/新闻网站编写代码。主页上有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()函数,它按预期工作。在cachearchivepages()函数中,当我将()更新到数据存储之后,在获取CharticlesPage()之前,是否会有太少的时间经过?也许写事务还没有完成,所以我得到了旧的结果?我尝试使用time.sleep()并在调用cachearchivepages()之前等待几秒钟,在这种情况下,我无法重现这种行为,但在我看来,time.sleep()不是一个好主意。不管怎样,我需要知道这种行为的确切原因以及如何处理。

最佳答案

你很可能会受到“最终一致的查询”的打击。在使用HR数据存储时,查询可能使用稍微旧的数据,而put()写入的数据对查询可见需要一段时间(get()by key或id不存在这样的延迟)。延迟通常是以秒为单位的,但我不认为我们可以保证有一个上限——我想,如果您遇到不幸的网络分区,可能需要几个小时。
有各种各样的部分解决方案,从最新的作者查看查询结果时的欺骗到使用祖先查询(它们有自己的限制)。您可以简单地给缓存一个有限的生存期,并在读而不是写时更新它。
祝你好运!

10-05 18:09
查看更多