2011-11-09 10 views
1

私はすなわち、自動的にキャッシュされているdb.Modelのサブクラスを作成することに取り組んできました:自動的にキャッシュされたモデル

  • instance.putは、データストアにそれを永続化する前にmemcacheにエンティティを格納します
  • class.get_by_key_nameは、最初にキャッシュをチェックします、そして逃した場合は、それを取得するために、データストアに行くと、私は(私のために働くように見えます)、以下のアプローチを開発し

検索した後、それをキャッシュし、だろうが私はいくつかの質問があります:

  1. 私はefficient model memcachingのNick Johnsonの記事を読んでいます。これは、プロトコルバッファを介してmemcacheのシリアル化を実装することを示唆しています。 SDKのmemcache APIソースコードを見ると、Googleは既にデフォルトでprotobufシリアル化を実装しているようです。私の解釈は正しいのですか?
  2. db.Modelをサブクラス化する方法と、2つのメソッドをオーバーライドする方法の中で、重要な詳細(将来的に私を得る可能性があります)が不足していますか?
  3. 以下に行ったことをより効率的に実装する方法はありますか?
  4. このようなエンティティキャッシングがパフォーマンスの観点から意味をなされる場合のガイドライン、ベンチマークまたはベストプラクティスはありますか?または、それは常にエンティティをキャッシュする意味ですか?関連するメモでは、私はGoogleがモデル化APIにキャッシュモデルを提供していないという事実を読んでいますか?考えるべき特別なケースがあまりにも多いのですか?

以下は現在の実装です。上記の4つの質問のうちの1つに対する直接的な回答ではなく、トピック全体に関連するものであっても、エンティティのキ​​ャッシングについてのあらゆる指針/提案を本当にありがとうと思います。

from google.appengine.ext import db 
from google.appengine.api import memcache 

import os 
import logging 

class CachedModel(db.Model): 
    '''Subclass of db.Model that automatically caches entities for put and 
    attempts to load from cache for get_by_key_name 
    ''' 

    @classmethod 
    def get_by_key_name(cls, key_names, parent=None, **kwargs): 
     cache = memcache.Client() 
     # Ensure that every new deployment of the application results in a cache miss 
     # by including the application version ID in the namespace of the cache entry 
     namespace = os.environ['CURRENT_VERSION_ID'] + '_' + cls.__name__ 

     if not isinstance(key_names, list): 
      key_names = [key_names] 
     entities = cache.get_multi(key_names, namespace=namespace) 
     if entities: 
      logging.info('%s (namespace=%s) retrieved from memcache' % (str(entities.keys()), namespace)) 

     missing_key_names = list(set(key_names) - set(entities.keys())) 
     # For keys missed in memcahce, attempt to retrieve entities from datastore 
     if missing_key_names: 
      missing_entities = super(CachedModel, cls).get_by_key_name(missing_key_names, parent, **kwargs) 
      missing_mapping = zip(missing_key_names, missing_entities) 
      # Determine entities that exist in datastore and store them to memcache 
      entities_to_cache = dict() 
      for key_name, entity in missing_mapping: 
       if entity: 
        entities_to_cache[key_name] = entity 
      if entities_to_cache: 
       logging.info('%s (namespace=%s) cached by get_by_key_name' % (str(entities_to_cache.keys()), namespace)) 
       cache.set_multi(entities_to_cache, namespace=namespace) 
      non_existent = set(missing_key_names) - set(entities_to_cache.keys()) 
      if non_existent: 
       logging.info('%s (namespace=%s) missing from cache and datastore' % (str(non_existent), namespace)) 
      # Combine entities retrieved from cache and entities retrieved from datastore 
      entities.update(missing_mapping) 

     if len(key_names) == 1: 
      return entities[key_names[0]] 
     else: 
      return [entities[key_name] for key_name in key_names] 

    def put(self, **kwargs): 
     cache = memcache.Client() 
     namespace = os.environ['CURRENT_VERSION_ID'] + '_' + self.__class__.__name__ 
     cache.set(self.key().name(), self, namespace=namespace) 
     logging.info('%s (namespace=%s) cached by put' % (self.key().name(), namespace)) 
     return super(CachedModel, self).put(**kwargs) 
+0

ちょっとあなたが望むことをする[シンプルなモジュール](https://github.com/ocanbascil/PerformanceEngine)を書いています。あなたはそれをチェックすることができます。 –

+0

@カーン - あなたのモデルを私に指摘してくれてありがとう。私は実際にmemcache APIを使ってこれを行うことに興味があります。なぜなら、これはキャッシングを処理するよりスケーラブルな方法だと思うからです。 – cv12

答えて

2

車輪を改造するのではなく、すでにモデルインスタンスのmemcachingを実装しているNDBに切り替えてみませんか?

+0

ニック - 正しい方向に私を指してくれてありがとう。これはまさに私が必要としていたものですが、私はそれを知らなかったのです。 1.6.0 SDKとランタイムに既に入っていることがわかります。これは素晴らしいことです。 – cv12

1

あなたはget_by_key_nameをオーバーライドする代わりとしてpre and post hooks for data model classesを追加するにはニック・ジョンソンの記事をチェックアウトすることがあります。これにより、db.getとdb.putを使用していてもフックが機能します。

私は、アプリ全体で、ページ全体をレンダリングするのに必要なすべてのコンテンツや、可能であればページのHTML自体など、より高レベルのものをキャッシュすることでパフォーマンスが大幅に向上していることがわかりました。

の結果をキャッシュするために、asynctoolsライブラリをチェックアウトすることもできます。

+0

pre/post hookアプローチに基づいて、モデルのキー(キー名ではなく)をmemcacheキーとして使用することをお勧めしますか?もしそうなら、私はstr()を使ってキーをエンコードする必要があると仮定します。もしあなたが明確にできるなら、私は感謝します。 – cv12

+0

キーが機能します。この場合、古い古いstr(キー)がそのトリックを行います。 –

0

Nick Johnson氏の優れたヒントは、モジュールappengine-mpに既に実装されています。 protocolbufやプリフェッチエンティティによるシリアライゼーションのようなものです。

あなたの方法についてget_by_key_namesあなたはcheck the codeです。独自のdb.Modelレイヤーを作成したい場合は、おそらくそれが役に立ちますが、既存のモデルを改善するために貢献することもできます。 ;)

+0

私にあなたのプロジェクトを指摘してくれてありがとう。私が知る限り、super()を呼び出して拡張するのではなく、get/put/deleteを完全に書き換えることでオーバーライドしています。このアプローチの私の懸念は、これが関連するAPIの将来のバージョンで問題を引き起こすかもしれないということです。私はできるだけ軽量なアプローチをとっていきたいと思います(つまり、Googleは自分の目標を達成するために必要な最小限の方法でGoogleにそのようにしてもらいます)。コードで使用したアプローチを誤解しましたか? – cv12

関連する問題