2012-06-16 9 views
15

ブログ/ニュースサイト用のコードを記述します。メインページには最新の記事が10件あります。また、すべての記事が変更時間の降順でソートされたアーカイブセクションがあります。アーカイブセクションでは、カーソルに基づくページネーションを使用し、新しい記事が公開されたときやページが何らかの理由でドラフトに移行したときにのみページが変更されるため、第2ページからの結果をキャッシュします。すべてのページには10個の記事があります。したがって、ユーザが(最初のものではなく)いくつかの番号を持つアーカイブページをヒットすると、memcacheはそのページ番号の結果が最初にチェックされます。ページがない場合は、memcacheのは、そのページのカーソルがチェックされ、その後、結果はそのカーソルを使用してデータストアからフェッチされる:put()後のApp Engine Datastoreでの読み込み遅延

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()関数内のfetchArticlesPage()の前にデータストアへの更新を挿入したあと、あまりにも時間がかかりすぎるのでしょうか?書き込みトランザクションがまだ完了していない可能性がありますので、古い結果が得られますか?私はtime.sleep()を使用してcacheArchivePages()を呼び出す前に数秒待っていましたが、その場合はその動作を再現できませんでしたが、time.sleep()は良い考えではないようです。とにかく、私はその行動の正確な原因とその対処方法を知る必要があります。

答えて

22

「最終的に一貫性のあるクエリ」が発生する可能性が最も高いです。 HRデータストアを使用する場合、クエリではわずかに古いデータが使用される可能性があり、put()によって書き込まれたデータがクエリに表示されるまでに時間がかかります(キーまたはIDによるget()の遅延はありません)。遅延は通常秒単位で測定されますが、私は上限を保証しているとは思いません。不幸なネットワークパーティションにぶつかった場合、数時間かかるかもしれません。

最新の書き込みの作成者がクエリ結果を祖先クエリ(独自の制限を共有している)を使用して表示しているときに、不正行為からあらゆる種類の解決法があります。キャッシュに限られた有効期間を与え、書き込みではなく読み込みで更新することができます。

幸運を祈る!

+0

ありがとうございました!私はデータの一貫性にもっと注意を払い、HRデータストアの特性を覚えておく必要があります。この特定のケースでは、古い結果と新しい結果を比較するのは簡単ですし、それらが同じであればクエリを再開することも簡単です。 – wombatonfire

+0

私は "祖先のクエリを使用する"正確に私のアプリで遅延の問題を解決したので、あなたがGuidoをありがとう、アップアップしました。 – Deleplace