私はすなわち、自動的にキャッシュされているdb.Modelのサブクラスを作成することに取り組んできました:自動的にキャッシュされたモデル
- instance.putは、データストアにそれを永続化する前にmemcacheにエンティティを格納します
- class.get_by_key_nameは、最初にキャッシュをチェックします、そして逃した場合は、それを取得するために、データストアに行くと、私は(私のために働くように見えます)、以下のアプローチを開発し
検索した後、それをキャッシュし、だろうが私はいくつかの質問があります:
- 私はefficient model memcachingのNick Johnsonの記事を読んでいます。これは、プロトコルバッファを介してmemcacheのシリアル化を実装することを示唆しています。 SDKのmemcache APIソースコードを見ると、Googleは既にデフォルトでprotobufシリアル化を実装しているようです。私の解釈は正しいのですか?
- db.Modelをサブクラス化する方法と、2つのメソッドをオーバーライドする方法の中で、重要な詳細(将来的に私を得る可能性があります)が不足していますか?
- 以下に行ったことをより効率的に実装する方法はありますか?
- このようなエンティティキャッシングがパフォーマンスの観点から意味をなされる場合のガイドライン、ベンチマークまたはベストプラクティスはありますか?または、それは常にエンティティをキャッシュする意味ですか?関連するメモでは、私は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)
ちょっとあなたが望むことをする[シンプルなモジュール](https://github.com/ocanbascil/PerformanceEngine)を書いています。あなたはそれをチェックすることができます。 –
@カーン - あなたのモデルを私に指摘してくれてありがとう。私は実際にmemcache APIを使ってこれを行うことに興味があります。なぜなら、これはキャッシングを処理するよりスケーラブルな方法だと思うからです。 – cv12