我为博客/新闻网站编写代码。主页上有 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()函数中获取ArticlesPage()之前,时间过得太少了?也许写入事务尚未完成,所以我得到旧结果?我尝试使用 time.sleep() 并在调用 cacheArchivePages() 之前等待几秒钟,在这种情况下,我无法重现该行为,但在我看来 time.sleep() 不是一个好主意。无论如何,我需要知道这种行为的确切原因以及如何处理它。
您很可能受到"最终一致性查询"的打击。使用 HR 数据存储时,查询可能会使用稍微旧的数据,并且 put() 写入的数据需要一段时间才能对查询可见(通过键或 id 对 get() 没有这样的延迟)。 延迟通常以秒为单位,但我认为我们不能保证上限 - 如果您受到不幸的网络分区的打击,我想可能是几个小时。
有各种各样的部分解决方案,从在最近写入的作者查看查询结果时作弊到使用祖先查询(它们有自己的限制)。 您可以简单地为缓存提供有限的生存期,并在读取而不是写入时更新它。
祝你好运!