Slim3のMemcacheを利用してDatastoreアクセスを少なくする その1
GAEでは、DatastoreへのRead、Writeともに課金されているので、なるべく減らしたい。
Memcacheを使えば、レスポンスを速くしつつ、Datastoreへのアクセスを減らすことができる。
DataAccessor
Slim3では、DataBaseの派生クラスのDAOでDatastoreにアクセスしているが、
さらに1つクラスを用意して、Memcacheを間に挟む。
ここでは、DataAccessorという名前にした。
public class DataAccessor<T extends Slim3Model> { private DaoBase<T> dao; public DataAccessor(DaoBase<T> dao) { this.dao = dao; } }
Slim3Modelインターフェース
ここで、Slim3Modelは
public interface Slim3Model { Key getKey(); Long getVersion(); }
というインターフェースで、Slim3でModelを作成すると自動生成されるインターフェースを明示したもの。
使い方としては、
public class BlogSlim3ModelDao extends DaoBase<BlogSlim3Model>{ private DataAccessor<BlogSlim3Model> dataAccessor = new DataAccessor<BlogSlim3Model>(this); public List<BlogSlim3Model> getModelList() { List<Key> keyList = query().asKeyList(); return dataAccessor.read(keyList); } }
のような形になる。
BlogSlim3ModelDaoはSlim3のgen-model-with-daoで自動生成される。
read実装
readは以下のように実装する。
public class DataAccessor<T extends Slim3Model> { private KeyMemcache memcache = new KeyMemcache(); public List<T> read(List<Key> keyList) { Map<Key, T> memcachedModelMap = memcache.getAll(keyList); Map<Key, T> daoModelMap = getDaoModelMap(keyList, memcachedModelMap); memcache.putAll(daoModelMap); List<T> modelList = new ArrayList<T>(); for (Key key : keyList) { if (memcachedModelMap.containsKey(key)) { modelList.add(memcachedModelMap.get(key)); } else if (daoModelMap.containsKey(key)) { modelList.add(daoModelMap.get(key)); } } return modelList; } private Map<Key, T> getDaoModelMap(List<Key> keyList, Map<Key, T> memcachedModelMap) { Collection<Key> subtractedCollection = CollectionUtils.subtract(keyList, memcachedModelMap.keySet()); List<Key> subtractedList = new ArrayList<Key>(subtractedCollection); return dao.getAsMap(subtractedList); } }
Daoにアクセスする前に、Memcacheに保存されていないか確認する。
MemcacheにはDatastoreのKeyクラスをMemcacheのKey、Slim3により自動生成されるModelクラスをValueとして保存する。
Datastoreからは、CollectionUtils.subtractでMemcacheから取得できなかったKeyのみを取り出して、getする。
DatastoreからgetしたdaoModelMapはmemcache.putAll(daoModelMap)で、Memcacheに保存して、次回以降Memcacheから取得できるようにする。
MemcacheとDaoから取得したModelはこのままでは、順番がバラバラなので、最後に順番を整理して返す。
KeyMemcacheクラス
Memcacheは型情報が無くなるので、KeyMemcacheというクラスを用意して、型情報を付与している。
public class DataAccessor<T extends Slim3Model> { private class KeyMemcache { public void putAll(Map<Key, T> values) { Map<Object, Object> objectMap = new HashMap<Object, Object>(); for (Entry<Key, T> entry : values.entrySet()) { Object key = entry.getKey(); Object model = entry.getValue(); objectMap.put(key, model); } Memcache.putAll(objectMap); } public Map<Key, T> getAll(List<Key> keyList) { Map<Key, T> map = new HashMap<Key, T>(); Map<Object, Object> memcachedMap = Memcache.getAll(keyList); for (Entry<Object, Object> entry : memcachedMap.entrySet()) { Key key = (Key) entry.getKey(); @SuppressWarnings("unchecked") T model = (T) entry.getValue(); map.put(key, model); } return map; } } }