AppEngineでsessionを有効にしていると遅くなる

Google App Engineのsession情報はDatastoreを使っているため若干遅い。セッションを使わないアプリならセッションを無効にしておいた方が良い。
appengine-web.xmlにてfalseする
※デフォルト(sessions-enabledの記述がない場合)ではセッションは無効になっている。


Datastoreを使うということはDatastoreTimeoutExceptionが発生する可能性があるということ。なので極力切っておいたほうがいいようだ。
あと、jspはセッションがデフォルトでは有効になっているようなので無効にしておいた方がよい。

<%@page pageEncoding="UTF-8" session="false"%>

今日のエントリは全部twitterの情報メモ。

追記

いくつかtwitterでツッコミを頂けたのでjspのセッション無効が意味があるのかをちょっと検証してみた。
検証内容としてはセッションを有効にしている場合にセッションをjspでset,getしていないにも関わらずDatastoreへのアクセスが発生するのかどうか。先に結論を言うとjspのセッションを無効にするのは意味がないと思う。

検証方法はmakeSyncCallをフックしてDatastoreへのアクセスが発生したらログを出力するようにする。AppEngineではDatastoreやMemcacheなどサービスへのアクセスが発生した場合はmakeSyncCallというMethodが実行される仕組みになっている。このmakeSyncCallはApiProxy.setDelegate()を使う事で実行する処理を変更できる。今回使用したソースは下記。

import java.util.concurrent.Future;
import java.util.logging.Logger;

import com.google.apphosting.api.ApiProxy;
import com.google.apphosting.api.ApiProxy.ApiConfig;
import com.google.apphosting.api.ApiProxy.ApiProxyException;
import com.google.apphosting.api.ApiProxy.Delegate;
import com.google.apphosting.api.ApiProxy.Environment;
import com.google.apphosting.api.ApiProxy.LogRecord;

public class TestDelegate implements Delegate<Environment> {

    private static final Logger logger =
        Logger.getLogger(TestDelegate.class.getName());

    private static Delegate<Environment> originalDelegate = ApiProxy.getDelegate();

    public void setUp() throws Exception {
        logger.info("setUp");
        ApiProxy.setDelegate(this);
    }

    public byte[] makeSyncCall(Environment env, String service, String method,
            byte[] requestBuf) throws ApiProxyException {
        logger.info("makeSyncCall/" + service + "/" + method);
        return originalDelegate.makeSyncCall(env, service, method, requestBuf);
    }

    public Future<byte[]> makeAsyncCall(Environment env, String service,
            String method, byte[] requestBuf, ApiConfig config) {
        logger.info("makeAsyncCall/" + service + "/" + method);
        return originalDelegate.makeAsyncCall(
            env,
            service,
            method,
            requestBuf,
            config);
    }

    public void log(Environment env, LogRecord rec) {
        originalDelegate.log(env, rec);
    }
}

自分のアプリケーションのどこかでnew TestDelegate().setUp();を実行した後のサービスは必ず上記のクラスのmakeSyncCallを経由するようになる。jspにはsession="false"は記載しなかった。この仕組みで検証した結果次のようになった。

■appengine-web.xmlにてtrue

  • session変数にアクセスしなかった場合のリクエスト
  1. makeSyncCall/memcache/Get
  2. makeSyncCall/memcache/Set

※session変数にアクセスしなくてもmemcacheのアクセスが発生しているが、Datastoreへのアクセスは発生していない。

  • session変数にアクセスした場合のリクエスト
  1. makeSyncCall/memcache/Get
  2. makeSyncCall/datastore_v3/Put
  3. makeSyncCall/memcache/Set

※session変数にアクセスするとDatastoreへのアクセスが発生する。

また、appengine-web.xmlにてfalseの場合とsessions-enabledタグ自体をなくした場合でも同様の実験をしたところセッションが無効な状態で、session変数にアクセスしなかった場合のリクエストはmemcacheへのアクセスも発生しなかった。この実験をするまでは、セッション変数を使っても使わなくてもとりあえずセッション変数は有効にしておけばいいかなと思っていたのだが、上記結果の通りセッションを使わなくてもmemcacheへのアクセスが発生しコストがかかることがわかった。可能な限りセッションは使わない方がAppEngineではいいようだ。


そうそう、セッションを使う場合の注意点としてほかにもセッションはDatastoreに登録されるため定期的に削除する必要がある。AppEngineでは下記URLを実行するとセッション情報を消してくれる便利な機能がある。
/_ah/sessioncleanup?clear

セッションを使う場合は下記のようにcron.xmlで60分毎に削除などの設定をしておいた方がいいだろう。

  <cron>
    <url>/_ah/sessioncleanup?clear</url>
    <description>Session cleanup every 60 minutes</description>
    <schedule>every 60 minutes</schedule>
  </cron>

あわせてweb.xmlにて下記のセキュリティ設定もしておいた方がいい。セキュリティ設定をしていない場合は上記のURLを実行されて意図せずセッションを消される恐れがある。

    <security-constraint>
       <web-resource-collection>
           <url-pattern>/_ah/*</url-pattern>
       </web-resource-collection>
       <auth-constraint>
           <role-name>admin</role-name>
       </auth-constraint>
    </security-constraint>