SlideShare una empresa de Scribd logo
1 de 55
Descargar para leer sin conexión
Datastoreへのアクセスを楽して
         Memcacheアクセスに置き換える
                      ライブラリ作った in GAE/J
                          appengine ja night #24


                                          @v vakame


Wednesday, April 10, 13
自己紹介

                          わかめ まさひろ
                                   @v vakame
                                 GAE/J

                           TypeScript

                              AngularJS
Wednesday, April 10, 13
GAEもお金がかかる

                                課金額に困るぐらいの
                               人気アプリ作りたい…orz




Wednesday, April 10, 13
Money=√Evil
                                抜粋 http://goo.gl/AA9BA




                               細かい説明はshin1さんの資料参照
                                  http://goo.gl/DzkVW




Wednesday, April 10, 13
この辺削りたい…

                     • Entity Get
                      • 1 read
                     • Run Query
                      • 1 read + 1 read per entity retrieved
                     • Run Query (keys only)
                      • 1 read + 1 small per key retrieved
                                          Writeは削減できないけど
                                        Readなら…Readならきっと…!

Wednesday, April 10, 13
Memcacheを活用しよう!

                          Memcache = 無料!




Wednesday, April 10, 13
Memcacheを活用する
                @Test
                public void cacheEntity() throws EntityNotFoundException {
                	   final DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
                	   final MemcacheService memcache = MemcacheServiceFactory.getMemcacheService();

                	     Key key;
                	     { // 保存時にMemcacheに突っ込んでおこう
                	     	   Entity entity = new Entity("sample");
                	     	   entity.setProperty("str", "Hello memcache!");
                	     	   datastore.put(entity);
                	     	   key = entity.getKey();

                	     	   memcache.put(key, entity);
                	     }
                	     { // 読出時にMemcacheをまずチェック!
                	     	   // あるかなー…?
                	     	   Entity entity = (Entity) memcache.get(key);
                	     	   if (entity == null) {
                	     	   	   // なかったわー
                                                                          全ての操作を
                	     	   	   entity = datastore.get(key);                こんな感じに!
                	     	   }
                	     }
                }

Wednesday, April 10, 13
こんな操作だよね
                     • Datastoreにデータを読出 and 保存を行う

                     • Memcacheからデータを読む

                     • Memcacheからデータが取れなかった場合、
                          Datastoreからデータを取ってくる

                     • (データの操作とか)

                     • Datastoreに保存する

                     • Memcacheに保存する

Wednesday, April 10, 13
Queryキャッシュしたり
                @Test
                public void cacheQuery() throws EntityNotFoundException {
                    前略 2件 sample kind の Entity を保存しました。
                	     final MemcacheService memcache = MemcacheServiceFactory.getMemcacheService();
                	     { // 初回はキャッシュされていない
                	     	   Query query = new Query("sample");
                	     	   List<Entity> list = (List<Entity>) memcache.get(query);
                	     	   if (list == null) {false
                	     	   	   list = datastore.prepare(query).asList(FetchOptions.Builder.withDefaults());
                	     	   	   memcache.put(query, list);
                	     	   }
                	     	   assertThat("2件検索される", list.size(), is(2));
                	     }
                	     { // 2回目はMemcacheから読み出せる
                	     	   Query query = new Query("sample");
                	     	   List<Entity> list = (List<Entity>) memcache.get(query);
                	     	   if (list == null) {true
                	     	   	   list = datastore.prepare(query).asList(FetchOptions.Builder.withDefaults());
                	     	   	   memcache.put(query, list);
                	     	   }
                	     	   assertThat("2件検索される", list.size(), is(2));           Queryも
                	     }                                                      キャッシュ
                }

Wednesday, April 10, 13
正しい状態を保つ

                      @Test
                      public void cacheQueryWithCleanup() throws EntityNotFoundException {
                          前略 Query をキャッシュしてあります
                      	   {
                      	   	   Entity entity = new Entity("sample");
                      	   	   entity.setProperty("str", "Good night!");
                      	   	   datastore.put(entity);

                      	   	   // sample kind に Put があったらキャッシュ消す
                      	   	   Query query = new Query("sample");
                      	   	   memcache.delete(query);
                      	   	   @SuppressWarnings("unchecked")
                      	   	   List<Entity> list = (List<Entity>) memcache.get(query);
                      	   	   assertThat("キャッシュ消去済", list, nullValue());
                      	   }
                      }
                                                                          Putがあったら消す
                                                                          全ての箇所で

Wednesday, April 10, 13
Memcacheを活用する
                            課金が減るよ!


                           やったね
                          わかめちゃん!




Wednesday, April 10, 13
めんどくさ…orz
                           テンプレコード多すぎ…
                           毎回毎回ガンバルの辛い…
                           規則もジャンバリ増えるし…
                           つらいわー…折れるわー…




Wednesday, April 10, 13
そこでオススメ



Wednesday, April 10, 13
Memvache

                     • めむばっしゅ と社内では読まれてます

                     • Datastoreの操作を勝手に書き換えます

                     • コードを変更する必要はありません

                                      v vakame の
                                      v入れただけ感

Wednesday, April 10, 13
使用例
            @Test
            public void test() throws EntityNotFoundException {
            	   final DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
            	   final MemcacheService memcache = MemcacheServiceFactory.getMemcacheService();

            	     Key key;
            	     { // 明示的にMemcacheは使っていないですよ。
            	     	       Entity entity = new Entity("sample");
            	     	       entity.setProperty("str", "Hello memcache!");

            	     	       assertThat("作成前", memcache.getStatistics().getItemCount(), is(0L));
            	     	       datastore.put(entity);
            	     	       assertThat("作成後", memcache.getStatistics().getItemCount(), not(0L));


            	     	   key = entity.getKey();
            	     }
            	     { // Memvacheさんありがとう                                            It’s magic!
            	     	   Entity entity = datastore.get(key);
            	     	   assertThat(entity, notNullValue());
            	     }
            	     assertThat("見かけ上のRPC",      counter2.countMap.get("datastore_v3@Get"), is(1));
            	     assertThat("Memvache適用後", counter1.countMap.get("datastore_v3@Get"), is(0));
            }

Wednesday, April 10, 13
どうやって?



Wednesday, April 10, 13
GAEの構造




                                  僕らのアプリが動くAppServerは
       Google IO 2009 セッションより抜粋   別マシン上で動く色々なサービスと
       http://goo.gl/8Qrx8
                                     連携して動作します!
Wednesday, April 10, 13
裏でデータ流れる

                                データくれ∼
             僕らのアプリ                                     Datastore
                                RPC by TCP/IP
                          マシン                (たぶん)       マシン
                                  はいよ∼


                                      Remote Procedure Call は
                                 Protocol Buffers でSerializeされてから
                                        やり取りされてます
Wednesday, April 10, 13
Delegateさーん!
   @Test
   public void delegate() {
   	   final Delegate<Environment> original = ApiProxy.getDelegate();
   	   ApiProxy.setDelegate(new DelegateStub(original) {
            // DelegateStubはDelegateインタフェースのゴチャゴチャを適当に元のDelegateに投げるよう実装した自前クラスです。
   	     	     @Override
   	     	     public byte[] makeSyncCall(Environment environment, String packageName, String methodName, byte[] request)
   	     	     	   	    throws ApiProxyException {
   	     	     	   logger.info("sync packageName=" + packageName + ", methodName=" + methodName);
   	     	     	   return super.makeSyncCall(environment, packageName, methodName, request);
   	     	     }

   	     	     @Override
   	     	     public Future<byte[]> makeAsyncCall(Environment environment, String packageName, String methodName,
                        byte[] request, ApiConfig apiConfig) {
   	     	     	   logger.info("async packageName=" + packageName + ", methodName=" + methodName);
   	     	     	   return super.makeAsyncCall(environment, packageName, methodName, request, apiConfig);
   	     	     }
   	     });

   	     final DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
   	     Entity entity = new Entity("sample");
   	     // INFO: async packageName=datastore_v3, methodName=Put
   	     datastore.put(entity);
                                                                           各種RPCの通信内容を
   	     ApiProxy.setDelegate(original);
   }                                                                           傍受できるよ!

Wednesday, April 10, 13
PBでdeserializeする
                    @Test
                    public void deserialize() {
                    	   // 1つ前のスライドから変更なしです!
                    	     	   @Override
                    	     	   public Future<byte[]> makeAsyncCall(Environment environment, String packageName,
                                       String methodName, byte[] request, ApiConfig apiConfig) {

                    	     	   	  if ("datastore_v3".equals(packageName) && "Put".equals(methodName)) {
                    	     	   	  	   PutRequest requestPb = new PutRequest();
                    	     	   	  	   requestPb.mergeFrom(request);                 entity <
                    	     	   	  	   logger.info(requestPb.toString());
                                                                                     key <
                    	     	   	  }                                                     app: "Unit Tests"
                    	     	   	  // 1つ前のスライドから変更なしです!
                                                                                       path <
                    	     	   }                                                          Element {
                    	     // 1つ前のスライドから変更なしです!                                              type: "sample"
                    }                                                                    }
                                                                                       >
                                                                                     >
                                                                                     entity_group <

                                                                                        >
                                                                                    >


                                                             packageNameとmethodNameの
                                                           組み合わせ毎にクラスが代わります


Wednesday, April 10, 13
色々な種類があるよ
                           if ("datastore_v3".equals(service) && "BeginTransaction".equals(method)) {
                           	    BeginTransactionRequest requestPb = new BeginTransactionRequest();
                           	    requestPb.mergeFrom(request);
                           	    return pre_datastore_v3_BeginTransaction(requestPb);
                           } else if ("datastore_v3".equals(service) && "Put".equals(method)) {
                           	    PutRequest requestPb = new PutRequest();
                           	    requestPb.mergeFrom(request);
                           	    return pre_datastore_v3_Put(requestPb);
                           } else if ("datastore_v3".equals(service) && "Get".equals(method)) {
                           	    GetRequest requestPb = new GetRequest();
                           	    requestPb.mergeFrom(request);
                           	    return pre_datastore_v3_Get(requestPb);
                           } else if ("datastore_v3".equals(service) && "Delete".equals(method)) {
                           	    DeleteRequest requestPb = new DeleteRequest();
                           	    requestPb.mergeFrom(request);
                           	    return pre_datastore_v3_Delete(requestPb);
                           } else if ("datastore_v3".equals(service) && "RunQuery".equals(method)) {
                           	    Query requestPb = new Query();
                           	    requestPb.mergeFrom(request);
                           	    return pre_datastore_v3_RunQuery(requestPb);
                           } else if ("datastore_v3".equals(service) && "Next".equals(method)) {
                           	    NextRequest requestPb = new NextRequest();
                           	    requestPb.mergeFrom(request);
                           	    return pre_datastore_v3_Next(requestPb);
                           } else if ("datastore_v3".equals(service) && "Commit".equals(method)) {
                           	    Transaction requestPb = new Transaction();
                           	    requestPb.mergeFrom(request);
                           	    return pre_datastore_v3_Commit(requestPb);
                           } else if ("datastore_v3".equals(service) && "Rollback".equals(method)) {
                           	    Transaction requestPb = new Transaction();
                           	    requestPb.mergeFrom(request);
                           	    return pre_datastore_v3_Rollback(requestPb);
                           } else if ("memcache".equals(service) && "Set".equals(method)) {
                           	    try {
                           	    	     MemcacheSetRequest requestPb = MemcacheSetRequest.parseFrom(request);
                           	    	     return pre_memcache_Set(requestPb);
                           	    } catch (com.google.appengine.repackaged.com.google.protobuf.InvalidProtocolBufferException e) {
                           	    	     throw new IllegalStateException("raise exception at " + service + ", " + method, e);
                           	    }
                           } else if ("memcache".equals(service) && "Get".equals(method)) {
Wednesday, April 10, 13
裏でコレやる!
                     • Datastoreにデータを読出 and 保存を行う

                     • Memcacheからデータを読む

                     • Memcacheからデータが取れなかった場合、
                          Datastoreからデータを取ってくる

                     • (データの操作とか)

                     • Datastoreに保存する

                     • Memcacheに保存する             再掲


Wednesday, April 10, 13
Memvacheについて



Wednesday, April 10, 13
戦略
                     • GetとPutの置き換え

                          • GetPutCacheStrategy.java
                     • Queryを自動的にKeysOnlyに書き換え

                          • QueryKeysOnlyStrategy.java
                     • Queryまるごとキャッシュ

                          • AggressiveQueryCacheStrategy.java
                          • デフォルト無効…
                                                          wiki
Wednesday, April 10, 13
その他
                     • MemvacheFilter.java
                          • Filterとして実装されている

                          • オプションもここで読込

                     • MemvacheDelegate.java
                          • “memvache” 名前空間にデータを貯めこむ

                          • Strategyの追加・削除もここで


Wednesday, April 10, 13
GetPutCacheStrategy
                     • MemcacheのKey = EntityのKey

                     • Get, Put を覗き見していい感じにする

                          • Memcacheに蓄えたり

                          • キャッシュあったらそれ返したり

                     • Tx下だったら全部素通しする

                          • じゃないとTx適用されないからね…

Wednesday, April 10, 13
QueryKeysOnlyStrategy
                     • Entityも取得するQueryを書き換える

                          • KeysOnlyに書き換える

                          • Keyゲットしたら後はBatchGet

                          • PutGetCacheStrategyさーん!

                     • EventualなEntityとれる問題も回避!
                                      ajn #23 で言及がありましたが
                                    Queryは古い内容取れる時があるそうな
Wednesday, April 10, 13
QueryKeysOnlyStrategy
                     • EventualなEntityとれる問題?

                          • Queryは古いEntityが取れる時がある

                           • index更新遅れ…なんてチャチな(ry

                          • でもデータ超大量の時だけらしい?

                          • KeysOnly+BatchGet = Strong!
                           • 少なくともEntityの内容は正しい

Wednesday, April 10, 13
AggressiveQueryCacheStrategy
                     • Queryをまるごと自動でキャッシュ!
                     • Kindが更新されたら消さないと…
                          • !参照できなけりゃよくね?
                          • Kind単位にカウンタを持つ
                          • EntityがPutされたら+1
                          • MemcacheのKeyにカウンタを混ぜる
                     • 現在デフォ無効(→あとで詳しく)
                                           Namespace単位での
                                           clearAllが欲しい…
Wednesday, April 10, 13
導入方法



Wednesday, April 10, 13
ダウンロード
         ソースコード
           https://github.com/vvakame/memvache
         バイナリ
           http://goo.gl/PYUw8



                                  mvn, gradle ユーザは
                                 net.vvakame:memvache




Wednesday, April 10, 13
ダウンロード




                              適当にクラスパスへ




Wednesday, April 10, 13
まずはテストに!
                          public class MemvacheAppEngineTestCase extends AppEngineTestCase {
                          	   MemvacheDelegate delegate;

                          	   @Before
                          	   @Override
                          	   public void setUp() throws Exception {
                          	   	   super.setUp();

                          	   	   delegate = MemvacheDelegate.install();
                          	   }

                          	   @After
                          	   @Override
                          	   public void tearDown() throws Exception {
                          	   	   delegate.uninstall();
                                                                            既存テストに
                          	   	   super.tearDown();
                          	   }                                            突っ込んでみよう!
                          }

                                                                       落ちないはずなので
                                                                   もし落ちたらIssueへ…
Wednesday, April 10, 13
web.xmlでの設定

              <filter>
              	 <filter-name>memvache</filter-name>
              	 <filter-class>net.vvakame.memvache.MemvacheFilter</filter-class>
              </filter>
              <filter-mapping>
              	 <filter-name>memvache</filter-name>
              	 <url-pattern>/*</url-pattern>
              </filter-mapping>



                                                    普通にフィルタを
                                                     設定してください



Wednesday, April 10, 13
web.xmlでの設定
          <filter>
          	 <filter-name>memvache</filter-name>
          	 <filter-class>net.vvakame.memvache.MemvacheFilter</filter-class>
          	 <init-param>
          	 	 <param-name>enableGetPutCacheStrategy</param-name>
          	 	 <param-value>true</param-value>
          	 </init-param>
          	 <init-param>
          	 	 <param-name>enableQueryKeysOnlyStrategy</param-name>
          	 	 <param-value>true</param-value>
          	 </init-param>
          	 <init-param>
          	 	 <param-name>enableAggressiveQueryCacheStrategy</param-name>
          	 	 <param-value>false</param-value>
          	 </init-param>
          	 <init-param>
          	 	 <param-name>enableDebugMode</param-name>
          	 	 <param-value>true</param-value>
          	 </init-param>
          </filter>                                オプションもあるよ!

Wednesday, April 10, 13
自作Strategyを追加
                      @Before
                      @Override
                      public void setUp() throws Exception {
                      	   MemvacheDelegate.addStrategy(OreOre1Strategy.class);

                      	   super.setUp();
                      }

                      public static class OreOre1Strategy implements Strategy {
                      	   @Override
                      	   public Pair<byte[], byte[]> preProcess(String packageName, String method,
                                  byte[] request) {
                      	   	   // Pair.request でリクエストを書き換える
                      	   	   // Pair.response でレスポンスを生成して返す
                      	   	   // null を返して何もしない
                      	   	   return null;
                      	   }
                      	   @Override
                      	   public byte[] postProcess(String packageName, String method,
                                  byte[] request, byte[] response) {
                      	   	   // レスポンスを書き換えて返す
                      	   	   // null を返して何もしない
                                                                 利用のために自作Filterを
                      	   	   return null;
                      	   }
                      }
                                                                 作成するのが良さげカナ
Wednesday, April 10, 13
自作Strategyを追加
                     @Before
                     @Override
                     public void setUp() throws Exception {
                     	   MemvacheDelegate.addStrategy(OreOre2Strategy.class);

                     	    super.setUp();
                     }

                     public static class OreOre2Strategy extends RpcVisitor {

                     	    @Override
                     	    public Pair<byte[], byte[]> pre_datastore_v3_Put(PutRequest requestPb) {
                     	    	   // packageName, method ごとに変換済のオブジェクトが渡される。後は Strategy と変わらない。
                     	    	   return null;
                     	    }

                     	    @Override
                     	    public byte[] post_datastore_v3_Put(PutRequest requestPb,
                     	    	   	  PutResponse responsePb) {
                     	    	   // 同上 足りないものは pull request 待ってます✩
                     	    	   return null;
                     	    }                                        便利なヘルパクラスも
                     }
                                                                      用意してあります
Wednesday, April 10, 13
Strategy固有設定
                • AggressiveQueryCacheStrategy
                     • memvache.properties
                          • expireSecod
                           • Memcacheに保持する期間

                          • ignoreKind
                           • キャッシュせずに素通しするKind
                          expireSecond=100             将来的に名前を
                          ignoreKind=ignore1,ignore2   変更するかも…


Wednesday, April 10, 13
問題点



Wednesday, April 10, 13
問題点
                     • Slim3以外での利用例が無い
                          • JPAとか生LowLevelAPIとか…
                          • わかめがSlim3と生LL APIの仕様の
                            区別があまりついてない
                          • Projection Queryは考慮対象外
                     • プロジェクト途中からの導入事例なし
                          • 新規プロジェクトでしか導入してない

Wednesday, April 10, 13
問題点
                     • AggressiveQueryCacheStrategy…
                      • 現在デフォルトで無効
                          • datastore_v3#Next が… orz
                           • 内部的に状態を持ってるので
                             不用意にキャッシュできなさそう…
                    FetchOptions fetchOptions =
                    FetchOptions.Builder.withLimit(10);               NextRequest requestPb

                    fetchOptions.prefetchSize(1);                      cursor <
                    if (cursor != null) {                                cursor: 0x0
                    	   fetchOptions.startCursor(cursor);                app: "Unit Tests"
                    }                                                  >
                    list = prepare.asQueryResultList(fetchOptions);    count: 0x7ffffffe
                    System.out.println("list size=" + list.size());    compile: true
                    totalLength += list.size();

Wednesday, April 10, 13
問題点
                     • AggressiveQueryCacheStrategy(続き)
                          • CursorにはID的なものが振られる
                           • キャッシュしたQueryだとIDが…
                           • 後でNextが発生するとヤバい><
                          • limit<prefetchSize ならOK…?
                           • 教えて識者の人><
                               Queryで10Entity取得
 = 1 read + 10 read
                               KeysOnly+Memcache	 = 1 read + 10 small ops
                               AggressiveQuery(ry
 = 0 

Wednesday, April 10, 13
pull requestのお願い



Wednesday, April 10, 13
pull requestのお願い
                     • バグを見つけたら…

                     • 新しい良いStrategyを思いついたら…

                     • 性能の改善…

                     • RpcVisitorへのメソッドの追加
                                     code review と、
                     • etc, etc...   “問題なかったよ”
                                     報告も嬉しいです!

Wednesday, April 10, 13
閑話休題



Wednesday, April 10, 13
GAE活用事例



Wednesday, April 10, 13
GAE or Android
                     • TG社のお仕事
                          • Androidアプリ開発! 3∼4割
                          • GAE/J (Apps抜き)! 2∼3割
                                          !
                          • GAE/J (Apps有り)! 2∼3割
                                          !
                          • GAE/J + EC2!! ! ! 1割
                           • 今後はGAE/J + GCE かなぁ
                                    TG社はジャンバリGAEです!
                                    Memvacheも普通に使っていきます。

Wednesday, April 10, 13
BizReport

                     • 伊藤忠テクノソリューションズ様の
                          SmartBiz+ を利用

                     • Android or iOS から簡単レポート作成

                     • 管理者向けUIなどでGAE/Jを利用



                                   http://bizreport.topgate.co.jp/
Wednesday, April 10, 13
Chienoki


                     • 社内ナレッジベース的な何か

                     • GAE+Appsを利用




                              http://chienoki.topgate.co.jp/lp
Wednesday, April 10, 13
Memvache...
                     • 現在2案件で利用中…
                          • 両方リリースはまだ出来ていない
                     • 個人利用もちらほら
                          • 俺とか元社員の人とか
                     • たまにIssueが発見・報告される
                          • ぐぬぬ……

                                    もっとみんな利用していってね!

Wednesday, April 10, 13
DatastoreV4について



Wednesday, April 10, 13
DatastoreV4…だと…!?

                          ヤバそう




                          頼む∼∼∼
                          Next氏∼∼∼∼
                          なくなってくだされ∼∼∼



                                  状態コワイ




Wednesday, April 10, 13
GAE/Goの正式リリース

                          いつなんですかねー…?
                          正式リリースされたら
                          TG社がGoガチ勢化との   も…
                           ↑だいたいおがわさんの犯行

Wednesday, April 10, 13
GAE/Pのndbずるい

                               PythonではMemvache的なものが
                               デフォルトであるそうじゃないで
                               すか!ずるい!流用可能なネタが
                               あったら教えてくださ(ry

Wednesday, April 10, 13
質問?


                  なにかあるかな?
                          スライド中のサンプルコード
                          github.com/vvakame/ajn24-sample


                              Googleグループ(アナウンス等)
                              http://goo.gl/GiQRJ


Wednesday, April 10, 13

Más contenido relacionado

La actualidad más candente

Android Lecture #03 @PRO&BSC Inc.
Android Lecture #03 @PRO&BSC Inc.Android Lecture #03 @PRO&BSC Inc.
Android Lecture #03 @PRO&BSC Inc.Yuki Higuchi
 
イマドキC++erのモテカワリソース管理術
イマドキC++erのモテカワリソース管理術イマドキC++erのモテカワリソース管理術
イマドキC++erのモテカワリソース管理術Kohsuke Yuasa
 
C++ マルチスレッドプログラミング
C++ マルチスレッドプログラミングC++ マルチスレッドプログラミング
C++ マルチスレッドプログラミングKohsuke Yuasa
 
Effective java 勉強会
Effective java 勉強会Effective java 勉強会
Effective java 勉強会Takinami Kei
 
Boostライブラリ一周の旅
Boostライブラリ一周の旅 Boostライブラリ一周の旅
Boostライブラリ一周の旅 Akira Takahashi
 
オープンソースでExcelレポートプログラミング
オープンソースでExcelレポートプログラミングオープンソースでExcelレポートプログラミング
オープンソースでExcelレポートプログラミングSho Okada
 
例外設計における大罪
例外設計における大罪例外設計における大罪
例外設計における大罪Takuto Wada
 

La actualidad más candente (7)

Android Lecture #03 @PRO&BSC Inc.
Android Lecture #03 @PRO&BSC Inc.Android Lecture #03 @PRO&BSC Inc.
Android Lecture #03 @PRO&BSC Inc.
 
イマドキC++erのモテカワリソース管理術
イマドキC++erのモテカワリソース管理術イマドキC++erのモテカワリソース管理術
イマドキC++erのモテカワリソース管理術
 
C++ マルチスレッドプログラミング
C++ マルチスレッドプログラミングC++ マルチスレッドプログラミング
C++ マルチスレッドプログラミング
 
Effective java 勉強会
Effective java 勉強会Effective java 勉強会
Effective java 勉強会
 
Boostライブラリ一周の旅
Boostライブラリ一周の旅 Boostライブラリ一周の旅
Boostライブラリ一周の旅
 
オープンソースでExcelレポートプログラミング
オープンソースでExcelレポートプログラミングオープンソースでExcelレポートプログラミング
オープンソースでExcelレポートプログラミング
 
例外設計における大罪
例外設計における大罪例外設計における大罪
例外設計における大罪
 

Más de Masahiro Wakame

GoCon2016 spring 自作Webフレームワーク uconを作った話
GoCon2016 spring 自作Webフレームワーク uconを作った話GoCon2016 spring 自作Webフレームワーク uconを作った話
GoCon2016 spring 自作Webフレームワーク uconを作った話Masahiro Wakame
 
GoCon 2015 Summer GoのASTをいじくって新しいツールを作る
GoCon 2015 Summer GoのASTをいじくって新しいツールを作るGoCon 2015 Summer GoのASTをいじくって新しいツールを作る
GoCon 2015 Summer GoのASTをいじくって新しいツールを作るMasahiro Wakame
 
ng-japan 2015 TypeScript+AngularJS 1.3
ng-japan 2015 TypeScript+AngularJS 1.3ng-japan 2015 TypeScript+AngularJS 1.3
ng-japan 2015 TypeScript+AngularJS 1.3Masahiro Wakame
 
TypeScriptは明日から使うべき
TypeScriptは明日から使うべきTypeScriptは明日から使うべき
TypeScriptは明日から使うべきMasahiro Wakame
 
TypeScript 型定義ファイルのある開発 TypeScript勉強会 VSハッカソン倶楽部
TypeScript 型定義ファイルのある開発 TypeScript勉強会 VSハッカソン倶楽部TypeScript 型定義ファイルのある開発 TypeScript勉強会 VSハッカソン倶楽部
TypeScript 型定義ファイルのある開発 TypeScript勉強会 VSハッカソン倶楽部Masahiro Wakame
 
Google Glassでできること XE12版 最新開発情報 Mirror API & GDK
Google Glassでできること XE12版 最新開発情報 Mirror API & GDKGoogle Glassでできること XE12版 最新開発情報 Mirror API & GDK
Google Glassでできること XE12版 最新開発情報 Mirror API & GDKMasahiro Wakame
 
コンパイラ指向ReVIEW
コンパイラ指向ReVIEWコンパイラ指向ReVIEW
コンパイラ指向ReVIEWMasahiro Wakame
 
20ヶ月を取り戻す Dart flight school
20ヶ月を取り戻す Dart flight school20ヶ月を取り戻す Dart flight school
20ヶ月を取り戻す Dart flight schoolMasahiro Wakame
 
Google Glassでできること XE11版 最新開発情報 Mirror API & GDK
Google Glassでできること XE11版 最新開発情報 Mirror API & GDKGoogle Glassでできること XE11版 最新開発情報 Mirror API & GDK
Google Glassでできること XE11版 最新開発情報 Mirror API & GDKMasahiro Wakame
 
CEATEC Glassware(Google Glassアプリ)開発の指南と開発事例
CEATEC Glassware(Google Glassアプリ)開発の指南と開発事例CEATEC Glassware(Google Glassアプリ)開発の指南と開発事例
CEATEC Glassware(Google Glassアプリ)開発の指南と開発事例Masahiro Wakame
 
ReVIEW & CI - ChefでCI環境構築
ReVIEW & CI - ChefでCI環境構築ReVIEW & CI - ChefでCI環境構築
ReVIEW & CI - ChefでCI環境構築Masahiro Wakame
 
Firefox OS勉強会 2nd TypeScript+AngularJS
Firefox OS勉強会 2nd TypeScript+AngularJSFirefox OS勉強会 2nd TypeScript+AngularJS
Firefox OS勉強会 2nd TypeScript+AngularJSMasahiro Wakame
 
AngularJS+TypeScript - AngularJS 1周年記念勉強会
AngularJS+TypeScript - AngularJS 1周年記念勉強会AngularJS+TypeScript - AngularJS 1周年記念勉強会
AngularJS+TypeScript - AngularJS 1周年記念勉強会Masahiro Wakame
 
Buildinsider OFFLINE TypeScriptの基礎から実践・利用事例まで
Buildinsider OFFLINE TypeScriptの基礎から実践・利用事例までBuildinsider OFFLINE TypeScriptの基礎から実践・利用事例まで
Buildinsider OFFLINE TypeScriptの基礎から実践・利用事例までMasahiro Wakame
 
俺とお前とGoogleware
俺とお前とGoogleware俺とお前とGoogleware
俺とお前とGooglewareMasahiro Wakame
 

Más de Masahiro Wakame (20)

GoCon2016 spring 自作Webフレームワーク uconを作った話
GoCon2016 spring 自作Webフレームワーク uconを作った話GoCon2016 spring 自作Webフレームワーク uconを作った話
GoCon2016 spring 自作Webフレームワーク uconを作った話
 
GoCon 2015 Summer GoのASTをいじくって新しいツールを作る
GoCon 2015 Summer GoのASTをいじくって新しいツールを作るGoCon 2015 Summer GoのASTをいじくって新しいツールを作る
GoCon 2015 Summer GoのASTをいじくって新しいツールを作る
 
ng-japan 2015 TypeScript+AngularJS 1.3
ng-japan 2015 TypeScript+AngularJS 1.3ng-japan 2015 TypeScript+AngularJS 1.3
ng-japan 2015 TypeScript+AngularJS 1.3
 
TypeScriptは明日から使うべき
TypeScriptは明日から使うべきTypeScriptは明日から使うべき
TypeScriptは明日から使うべき
 
TypeScript 型定義ファイルのある開発 TypeScript勉強会 VSハッカソン倶楽部
TypeScript 型定義ファイルのある開発 TypeScript勉強会 VSハッカソン倶楽部TypeScript 型定義ファイルのある開発 TypeScript勉強会 VSハッカソン倶楽部
TypeScript 型定義ファイルのある開発 TypeScript勉強会 VSハッカソン倶楽部
 
Google Glass XE17版
Google Glass XE17版Google Glass XE17版
Google Glass XE17版
 
Google Glassでできること XE12版 最新開発情報 Mirror API & GDK
Google Glassでできること XE12版 最新開発情報 Mirror API & GDKGoogle Glassでできること XE12版 最新開発情報 Mirror API & GDK
Google Glassでできること XE12版 最新開発情報 Mirror API & GDK
 
コンパイラ指向ReVIEW
コンパイラ指向ReVIEWコンパイラ指向ReVIEW
コンパイラ指向ReVIEW
 
20ヶ月を取り戻す Dart flight school
20ヶ月を取り戻す Dart flight school20ヶ月を取り戻す Dart flight school
20ヶ月を取り戻す Dart flight school
 
TypeScript 独習会
TypeScript 独習会TypeScript 独習会
TypeScript 独習会
 
Google Glassでできること XE11版 最新開発情報 Mirror API & GDK
Google Glassでできること XE11版 最新開発情報 Mirror API & GDKGoogle Glassでできること XE11版 最新開発情報 Mirror API & GDK
Google Glassでできること XE11版 最新開発情報 Mirror API & GDK
 
CEATEC Glassware(Google Glassアプリ)開発の指南と開発事例
CEATEC Glassware(Google Glassアプリ)開発の指南と開発事例CEATEC Glassware(Google Glassアプリ)開発の指南と開発事例
CEATEC Glassware(Google Glassアプリ)開発の指南と開発事例
 
ReVIEW & CI - ChefでCI環境構築
ReVIEW & CI - ChefでCI環境構築ReVIEW & CI - ChefでCI環境構築
ReVIEW & CI - ChefでCI環境構築
 
Firefox OS勉強会 2nd TypeScript+AngularJS
Firefox OS勉強会 2nd TypeScript+AngularJSFirefox OS勉強会 2nd TypeScript+AngularJS
Firefox OS勉強会 2nd TypeScript+AngularJS
 
AngularJS+TypeScript - AngularJS 1周年記念勉強会
AngularJS+TypeScript - AngularJS 1周年記念勉強会AngularJS+TypeScript - AngularJS 1周年記念勉強会
AngularJS+TypeScript - AngularJS 1周年記念勉強会
 
Buildinsider OFFLINE TypeScriptの基礎から実践・利用事例まで
Buildinsider OFFLINE TypeScriptの基礎から実践・利用事例までBuildinsider OFFLINE TypeScriptの基礎から実践・利用事例まで
Buildinsider OFFLINE TypeScriptの基礎から実践・利用事例まで
 
俺とお前とGoogleware
俺とお前とGoogleware俺とお前とGoogleware
俺とお前とGoogleware
 
TypeScript 勉強会
TypeScript 勉強会TypeScript 勉強会
TypeScript 勉強会
 
TypeScript Hands-on
TypeScript Hands-onTypeScript Hands-on
TypeScript Hands-on
 
わかめモナ化LT
わかめモナ化LTわかめモナ化LT
わかめモナ化LT
 

Último

Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介
Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介
Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介Yuma Ohgami
 
論文紹介:Semantic segmentation using Vision Transformers: A survey
論文紹介:Semantic segmentation using Vision Transformers: A survey論文紹介:Semantic segmentation using Vision Transformers: A survey
論文紹介:Semantic segmentation using Vision Transformers: A surveyToru Tamaki
 
Postman LT Fukuoka_Quick Prototype_By Daniel
Postman LT Fukuoka_Quick Prototype_By DanielPostman LT Fukuoka_Quick Prototype_By Daniel
Postman LT Fukuoka_Quick Prototype_By Danieldanielhu54
 
論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...
論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...
論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...Toru Tamaki
 
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略Ryo Sasaki
 
SOPを理解する 2024/04/19 の勉強会で発表されたものです
SOPを理解する       2024/04/19 の勉強会で発表されたものですSOPを理解する       2024/04/19 の勉強会で発表されたものです
SOPを理解する 2024/04/19 の勉強会で発表されたものですiPride Co., Ltd.
 
TSAL operation mechanism and circuit diagram.pdf
TSAL operation mechanism and circuit diagram.pdfTSAL operation mechanism and circuit diagram.pdf
TSAL operation mechanism and circuit diagram.pdftaisei2219
 
論文紹介:Automated Classification of Model Errors on ImageNet
論文紹介:Automated Classification of Model Errors on ImageNet論文紹介:Automated Classification of Model Errors on ImageNet
論文紹介:Automated Classification of Model Errors on ImageNetToru Tamaki
 
【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)
【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)
【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)Hiroki Ichikura
 
スマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システムスマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システムsugiuralab
 

Último (10)

Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介
Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介
Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介
 
論文紹介:Semantic segmentation using Vision Transformers: A survey
論文紹介:Semantic segmentation using Vision Transformers: A survey論文紹介:Semantic segmentation using Vision Transformers: A survey
論文紹介:Semantic segmentation using Vision Transformers: A survey
 
Postman LT Fukuoka_Quick Prototype_By Daniel
Postman LT Fukuoka_Quick Prototype_By DanielPostman LT Fukuoka_Quick Prototype_By Daniel
Postman LT Fukuoka_Quick Prototype_By Daniel
 
論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...
論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...
論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...
 
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
 
SOPを理解する 2024/04/19 の勉強会で発表されたものです
SOPを理解する       2024/04/19 の勉強会で発表されたものですSOPを理解する       2024/04/19 の勉強会で発表されたものです
SOPを理解する 2024/04/19 の勉強会で発表されたものです
 
TSAL operation mechanism and circuit diagram.pdf
TSAL operation mechanism and circuit diagram.pdfTSAL operation mechanism and circuit diagram.pdf
TSAL operation mechanism and circuit diagram.pdf
 
論文紹介:Automated Classification of Model Errors on ImageNet
論文紹介:Automated Classification of Model Errors on ImageNet論文紹介:Automated Classification of Model Errors on ImageNet
論文紹介:Automated Classification of Model Errors on ImageNet
 
【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)
【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)
【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)
 
スマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システムスマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システム
 

Datastoreへのアクセスを楽してMemcacheアクセスに置き換えるライブラリ作った

  • 1. Datastoreへのアクセスを楽して Memcacheアクセスに置き換える ライブラリ作った in GAE/J appengine ja night #24 @v vakame Wednesday, April 10, 13
  • 2. 自己紹介 わかめ まさひろ @v vakame GAE/J TypeScript AngularJS Wednesday, April 10, 13
  • 3. GAEもお金がかかる 課金額に困るぐらいの 人気アプリ作りたい…orz Wednesday, April 10, 13
  • 4. Money=√Evil 抜粋 http://goo.gl/AA9BA 細かい説明はshin1さんの資料参照 http://goo.gl/DzkVW Wednesday, April 10, 13
  • 5. この辺削りたい… • Entity Get • 1 read • Run Query • 1 read + 1 read per entity retrieved • Run Query (keys only) • 1 read + 1 small per key retrieved Writeは削減できないけど Readなら…Readならきっと…! Wednesday, April 10, 13
  • 6. Memcacheを活用しよう! Memcache = 無料! Wednesday, April 10, 13
  • 7. Memcacheを活用する @Test public void cacheEntity() throws EntityNotFoundException { final DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); final MemcacheService memcache = MemcacheServiceFactory.getMemcacheService(); Key key; { // 保存時にMemcacheに突っ込んでおこう Entity entity = new Entity("sample"); entity.setProperty("str", "Hello memcache!"); datastore.put(entity); key = entity.getKey(); memcache.put(key, entity); } { // 読出時にMemcacheをまずチェック! // あるかなー…? Entity entity = (Entity) memcache.get(key); if (entity == null) { // なかったわー 全ての操作を entity = datastore.get(key); こんな感じに! } } } Wednesday, April 10, 13
  • 8. こんな操作だよね • Datastoreにデータを読出 and 保存を行う • Memcacheからデータを読む • Memcacheからデータが取れなかった場合、 Datastoreからデータを取ってくる • (データの操作とか) • Datastoreに保存する • Memcacheに保存する Wednesday, April 10, 13
  • 9. Queryキャッシュしたり @Test public void cacheQuery() throws EntityNotFoundException { 前略 2件 sample kind の Entity を保存しました。 final MemcacheService memcache = MemcacheServiceFactory.getMemcacheService(); { // 初回はキャッシュされていない Query query = new Query("sample"); List<Entity> list = (List<Entity>) memcache.get(query); if (list == null) {false list = datastore.prepare(query).asList(FetchOptions.Builder.withDefaults()); memcache.put(query, list); } assertThat("2件検索される", list.size(), is(2)); } { // 2回目はMemcacheから読み出せる Query query = new Query("sample"); List<Entity> list = (List<Entity>) memcache.get(query); if (list == null) {true list = datastore.prepare(query).asList(FetchOptions.Builder.withDefaults()); memcache.put(query, list); } assertThat("2件検索される", list.size(), is(2)); Queryも } キャッシュ } Wednesday, April 10, 13
  • 10. 正しい状態を保つ @Test public void cacheQueryWithCleanup() throws EntityNotFoundException { 前略 Query をキャッシュしてあります { Entity entity = new Entity("sample"); entity.setProperty("str", "Good night!"); datastore.put(entity); // sample kind に Put があったらキャッシュ消す Query query = new Query("sample"); memcache.delete(query); @SuppressWarnings("unchecked") List<Entity> list = (List<Entity>) memcache.get(query); assertThat("キャッシュ消去済", list, nullValue()); } } Putがあったら消す 全ての箇所で Wednesday, April 10, 13
  • 11. Memcacheを活用する 課金が減るよ! やったね わかめちゃん! Wednesday, April 10, 13
  • 12. めんどくさ…orz テンプレコード多すぎ… 毎回毎回ガンバルの辛い… 規則もジャンバリ増えるし… つらいわー…折れるわー… Wednesday, April 10, 13
  • 14. Memvache • めむばっしゅ と社内では読まれてます • Datastoreの操作を勝手に書き換えます • コードを変更する必要はありません v vakame の v入れただけ感 Wednesday, April 10, 13
  • 15. 使用例 @Test public void test() throws EntityNotFoundException { final DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); final MemcacheService memcache = MemcacheServiceFactory.getMemcacheService(); Key key; { // 明示的にMemcacheは使っていないですよ。 Entity entity = new Entity("sample"); entity.setProperty("str", "Hello memcache!"); assertThat("作成前", memcache.getStatistics().getItemCount(), is(0L)); datastore.put(entity); assertThat("作成後", memcache.getStatistics().getItemCount(), not(0L)); key = entity.getKey(); } { // Memvacheさんありがとう It’s magic! Entity entity = datastore.get(key); assertThat(entity, notNullValue()); } assertThat("見かけ上のRPC", counter2.countMap.get("datastore_v3@Get"), is(1)); assertThat("Memvache適用後", counter1.countMap.get("datastore_v3@Get"), is(0)); } Wednesday, April 10, 13
  • 17. GAEの構造 僕らのアプリが動くAppServerは Google IO 2009 セッションより抜粋 別マシン上で動く色々なサービスと http://goo.gl/8Qrx8 連携して動作します! Wednesday, April 10, 13
  • 18. 裏でデータ流れる データくれ∼ 僕らのアプリ Datastore RPC by TCP/IP マシン (たぶん) マシン はいよ∼ Remote Procedure Call は Protocol Buffers でSerializeされてから やり取りされてます Wednesday, April 10, 13
  • 19. Delegateさーん! @Test public void delegate() { final Delegate<Environment> original = ApiProxy.getDelegate(); ApiProxy.setDelegate(new DelegateStub(original) { // DelegateStubはDelegateインタフェースのゴチャゴチャを適当に元のDelegateに投げるよう実装した自前クラスです。 @Override public byte[] makeSyncCall(Environment environment, String packageName, String methodName, byte[] request) throws ApiProxyException { logger.info("sync packageName=" + packageName + ", methodName=" + methodName); return super.makeSyncCall(environment, packageName, methodName, request); } @Override public Future<byte[]> makeAsyncCall(Environment environment, String packageName, String methodName, byte[] request, ApiConfig apiConfig) { logger.info("async packageName=" + packageName + ", methodName=" + methodName); return super.makeAsyncCall(environment, packageName, methodName, request, apiConfig); } }); final DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); Entity entity = new Entity("sample"); // INFO: async packageName=datastore_v3, methodName=Put datastore.put(entity); 各種RPCの通信内容を ApiProxy.setDelegate(original); } 傍受できるよ! Wednesday, April 10, 13
  • 20. PBでdeserializeする @Test public void deserialize() { // 1つ前のスライドから変更なしです! @Override public Future<byte[]> makeAsyncCall(Environment environment, String packageName, String methodName, byte[] request, ApiConfig apiConfig) { if ("datastore_v3".equals(packageName) && "Put".equals(methodName)) { PutRequest requestPb = new PutRequest(); requestPb.mergeFrom(request); entity < logger.info(requestPb.toString()); key < } app: "Unit Tests" // 1つ前のスライドから変更なしです! path < } Element { // 1つ前のスライドから変更なしです! type: "sample" } } > > entity_group < > > packageNameとmethodNameの 組み合わせ毎にクラスが代わります Wednesday, April 10, 13
  • 21. 色々な種類があるよ if ("datastore_v3".equals(service) && "BeginTransaction".equals(method)) { BeginTransactionRequest requestPb = new BeginTransactionRequest(); requestPb.mergeFrom(request); return pre_datastore_v3_BeginTransaction(requestPb); } else if ("datastore_v3".equals(service) && "Put".equals(method)) { PutRequest requestPb = new PutRequest(); requestPb.mergeFrom(request); return pre_datastore_v3_Put(requestPb); } else if ("datastore_v3".equals(service) && "Get".equals(method)) { GetRequest requestPb = new GetRequest(); requestPb.mergeFrom(request); return pre_datastore_v3_Get(requestPb); } else if ("datastore_v3".equals(service) && "Delete".equals(method)) { DeleteRequest requestPb = new DeleteRequest(); requestPb.mergeFrom(request); return pre_datastore_v3_Delete(requestPb); } else if ("datastore_v3".equals(service) && "RunQuery".equals(method)) { Query requestPb = new Query(); requestPb.mergeFrom(request); return pre_datastore_v3_RunQuery(requestPb); } else if ("datastore_v3".equals(service) && "Next".equals(method)) { NextRequest requestPb = new NextRequest(); requestPb.mergeFrom(request); return pre_datastore_v3_Next(requestPb); } else if ("datastore_v3".equals(service) && "Commit".equals(method)) { Transaction requestPb = new Transaction(); requestPb.mergeFrom(request); return pre_datastore_v3_Commit(requestPb); } else if ("datastore_v3".equals(service) && "Rollback".equals(method)) { Transaction requestPb = new Transaction(); requestPb.mergeFrom(request); return pre_datastore_v3_Rollback(requestPb); } else if ("memcache".equals(service) && "Set".equals(method)) { try { MemcacheSetRequest requestPb = MemcacheSetRequest.parseFrom(request); return pre_memcache_Set(requestPb); } catch (com.google.appengine.repackaged.com.google.protobuf.InvalidProtocolBufferException e) { throw new IllegalStateException("raise exception at " + service + ", " + method, e); } } else if ("memcache".equals(service) && "Get".equals(method)) { Wednesday, April 10, 13
  • 22. 裏でコレやる! • Datastoreにデータを読出 and 保存を行う • Memcacheからデータを読む • Memcacheからデータが取れなかった場合、 Datastoreからデータを取ってくる • (データの操作とか) • Datastoreに保存する • Memcacheに保存する 再掲 Wednesday, April 10, 13
  • 24. 戦略 • GetとPutの置き換え • GetPutCacheStrategy.java • Queryを自動的にKeysOnlyに書き換え • QueryKeysOnlyStrategy.java • Queryまるごとキャッシュ • AggressiveQueryCacheStrategy.java • デフォルト無効… wiki Wednesday, April 10, 13
  • 25. その他 • MemvacheFilter.java • Filterとして実装されている • オプションもここで読込 • MemvacheDelegate.java • “memvache” 名前空間にデータを貯めこむ • Strategyの追加・削除もここで Wednesday, April 10, 13
  • 26. GetPutCacheStrategy • MemcacheのKey = EntityのKey • Get, Put を覗き見していい感じにする • Memcacheに蓄えたり • キャッシュあったらそれ返したり • Tx下だったら全部素通しする • じゃないとTx適用されないからね… Wednesday, April 10, 13
  • 27. QueryKeysOnlyStrategy • Entityも取得するQueryを書き換える • KeysOnlyに書き換える • Keyゲットしたら後はBatchGet • PutGetCacheStrategyさーん! • EventualなEntityとれる問題も回避! ajn #23 で言及がありましたが Queryは古い内容取れる時があるそうな Wednesday, April 10, 13
  • 28. QueryKeysOnlyStrategy • EventualなEntityとれる問題? • Queryは古いEntityが取れる時がある • index更新遅れ…なんてチャチな(ry • でもデータ超大量の時だけらしい? • KeysOnly+BatchGet = Strong! • 少なくともEntityの内容は正しい Wednesday, April 10, 13
  • 29. AggressiveQueryCacheStrategy • Queryをまるごと自動でキャッシュ! • Kindが更新されたら消さないと… • !参照できなけりゃよくね? • Kind単位にカウンタを持つ • EntityがPutされたら+1 • MemcacheのKeyにカウンタを混ぜる • 現在デフォ無効(→あとで詳しく) Namespace単位での clearAllが欲しい… Wednesday, April 10, 13
  • 31. ダウンロード ソースコード https://github.com/vvakame/memvache バイナリ http://goo.gl/PYUw8 mvn, gradle ユーザは net.vvakame:memvache Wednesday, April 10, 13
  • 32. ダウンロード 適当にクラスパスへ Wednesday, April 10, 13
  • 33. まずはテストに! public class MemvacheAppEngineTestCase extends AppEngineTestCase { MemvacheDelegate delegate; @Before @Override public void setUp() throws Exception { super.setUp(); delegate = MemvacheDelegate.install(); } @After @Override public void tearDown() throws Exception { delegate.uninstall(); 既存テストに super.tearDown(); } 突っ込んでみよう! } 落ちないはずなので もし落ちたらIssueへ… Wednesday, April 10, 13
  • 34. web.xmlでの設定 <filter> <filter-name>memvache</filter-name> <filter-class>net.vvakame.memvache.MemvacheFilter</filter-class> </filter> <filter-mapping> <filter-name>memvache</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> 普通にフィルタを 設定してください Wednesday, April 10, 13
  • 35. web.xmlでの設定 <filter> <filter-name>memvache</filter-name> <filter-class>net.vvakame.memvache.MemvacheFilter</filter-class> <init-param> <param-name>enableGetPutCacheStrategy</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>enableQueryKeysOnlyStrategy</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>enableAggressiveQueryCacheStrategy</param-name> <param-value>false</param-value> </init-param> <init-param> <param-name>enableDebugMode</param-name> <param-value>true</param-value> </init-param> </filter> オプションもあるよ! Wednesday, April 10, 13
  • 36. 自作Strategyを追加 @Before @Override public void setUp() throws Exception { MemvacheDelegate.addStrategy(OreOre1Strategy.class); super.setUp(); } public static class OreOre1Strategy implements Strategy { @Override public Pair<byte[], byte[]> preProcess(String packageName, String method, byte[] request) { // Pair.request でリクエストを書き換える // Pair.response でレスポンスを生成して返す // null を返して何もしない return null; } @Override public byte[] postProcess(String packageName, String method, byte[] request, byte[] response) { // レスポンスを書き換えて返す // null を返して何もしない 利用のために自作Filterを return null; } } 作成するのが良さげカナ Wednesday, April 10, 13
  • 37. 自作Strategyを追加 @Before @Override public void setUp() throws Exception { MemvacheDelegate.addStrategy(OreOre2Strategy.class); super.setUp(); } public static class OreOre2Strategy extends RpcVisitor { @Override public Pair<byte[], byte[]> pre_datastore_v3_Put(PutRequest requestPb) { // packageName, method ごとに変換済のオブジェクトが渡される。後は Strategy と変わらない。 return null; } @Override public byte[] post_datastore_v3_Put(PutRequest requestPb, PutResponse responsePb) { // 同上 足りないものは pull request 待ってます✩ return null; } 便利なヘルパクラスも } 用意してあります Wednesday, April 10, 13
  • 38. Strategy固有設定 • AggressiveQueryCacheStrategy • memvache.properties • expireSecod • Memcacheに保持する期間 • ignoreKind • キャッシュせずに素通しするKind expireSecond=100 将来的に名前を ignoreKind=ignore1,ignore2 変更するかも… Wednesday, April 10, 13
  • 40. 問題点 • Slim3以外での利用例が無い • JPAとか生LowLevelAPIとか… • わかめがSlim3と生LL APIの仕様の 区別があまりついてない • Projection Queryは考慮対象外 • プロジェクト途中からの導入事例なし • 新規プロジェクトでしか導入してない Wednesday, April 10, 13
  • 41. 問題点 • AggressiveQueryCacheStrategy… • 現在デフォルトで無効 • datastore_v3#Next が… orz • 内部的に状態を持ってるので 不用意にキャッシュできなさそう… FetchOptions fetchOptions = FetchOptions.Builder.withLimit(10); NextRequest requestPb fetchOptions.prefetchSize(1); cursor < if (cursor != null) { cursor: 0x0 fetchOptions.startCursor(cursor); app: "Unit Tests" } > list = prepare.asQueryResultList(fetchOptions); count: 0x7ffffffe System.out.println("list size=" + list.size()); compile: true totalLength += list.size(); Wednesday, April 10, 13
  • 42. 問題点 • AggressiveQueryCacheStrategy(続き) • CursorにはID的なものが振られる • キャッシュしたQueryだとIDが… • 後でNextが発生するとヤバい>< • limit<prefetchSize ならOK…? • 教えて識者の人>< Queryで10Entity取得 = 1 read + 10 read KeysOnly+Memcache = 1 read + 10 small ops AggressiveQuery(ry = 0  Wednesday, April 10, 13
  • 44. pull requestのお願い • バグを見つけたら… • 新しい良いStrategyを思いついたら… • 性能の改善… • RpcVisitorへのメソッドの追加 code review と、 • etc, etc... “問題なかったよ” 報告も嬉しいです! Wednesday, April 10, 13
  • 47. GAE or Android • TG社のお仕事 • Androidアプリ開発! 3∼4割 • GAE/J (Apps抜き)! 2∼3割 ! • GAE/J (Apps有り)! 2∼3割 ! • GAE/J + EC2!! ! ! 1割 • 今後はGAE/J + GCE かなぁ TG社はジャンバリGAEです! Memvacheも普通に使っていきます。 Wednesday, April 10, 13
  • 48. BizReport • 伊藤忠テクノソリューションズ様の SmartBiz+ を利用 • Android or iOS から簡単レポート作成 • 管理者向けUIなどでGAE/Jを利用 http://bizreport.topgate.co.jp/ Wednesday, April 10, 13
  • 49. Chienoki • 社内ナレッジベース的な何か • GAE+Appsを利用 http://chienoki.topgate.co.jp/lp Wednesday, April 10, 13
  • 50. Memvache... • 現在2案件で利用中… • 両方リリースはまだ出来ていない • 個人利用もちらほら • 俺とか元社員の人とか • たまにIssueが発見・報告される • ぐぬぬ…… もっとみんな利用していってね! Wednesday, April 10, 13
  • 52. DatastoreV4…だと…!? ヤバそう 頼む∼∼∼ Next氏∼∼∼∼ なくなってくだされ∼∼∼ 状態コワイ Wednesday, April 10, 13
  • 53. GAE/Goの正式リリース いつなんですかねー…? 正式リリースされたら TG社がGoガチ勢化との も… ↑だいたいおがわさんの犯行 Wednesday, April 10, 13
  • 54. GAE/Pのndbずるい PythonではMemvache的なものが デフォルトであるそうじゃないで すか!ずるい!流用可能なネタが あったら教えてくださ(ry Wednesday, April 10, 13
  • 55. 質問? なにかあるかな? スライド中のサンプルコード github.com/vvakame/ajn24-sample Googleグループ(アナウンス等) http://goo.gl/GiQRJ Wednesday, April 10, 13