Moodleのセッションハンドリング

#Moodleでのセッション管理
##セッション管理の設定場所
Moodleでは独自にセッション管理を行っているため、php.iniなどでPHPのセッション情報の保存先変更などを行っていても上書きされる。
そのため、config.phpに規定された内容を記載してセッション管理設定を変更する必要がある。

##セッション情報保存先の種類
- file: **moodledata/sessionsディレクトリ**（変更可）内にファイルとして保存する。実際はメモリ上に収まると考えられるため、シングルサーバ構成でおそらく最適。しかし、moodledataディレクトリがNFS上などにある場合は速度低下およびファイルロックの問題があるため不適。
- database: Moodle用データベースの**sessionsテーブル**に記録される。設定が楽だが、同時利用者が多いと簡単にパフォーマンスのボトルネックになる。
- memcached: **Memcachedサーバ**と**php-pecl-memcached**を利用する。
- memcache: **php-pecl-memcached**の代わりに**php-pecl-memcache**を利用する。セッションロックが不可能なため非推奨（config-dist.phpに記載）らしい。
  - [Moodle 3.6から非対応になった](https://tracker.moodle.org/browse/MDL-63329)。
- redis: **Redisサーバ**と**php-pecl-redis**を利用する。memcached/memcacheと異なり、MUC(Moodle Universal Cache)と同居可能。

##セッション情報保存先の決定
Moodleでのセッション情報の保存先は以下の部分で決定される。

```php:lib/classes/session/manager.php
    /**
     * Create handler instance.
     */
    protected static function load_handler() {
        global $CFG, $DB;
        if (self::$handler) {
            return;
        }
        // Find out which handler to use.
        if (PHPUNIT_TEST) {
            $class = '\core\session\file';
        } else if (!empty($CFG->session_handler_class)) {
            $class = $CFG->session_handler_class;
        } else if (!empty($CFG->dbsessions) and $DB->session_lock_supported()) {
            $class = '\core\session\database';
        } else {
            $class = '\core\session\file';
        }
        self::$handler = new $class();
    }
```

読んでの通り、デフォルトではfileとなり、別途定義されていればそちらが利用される。$CFG->dbsessionsは過去との互換性（と簡易設定）のために用意されている模様。

##注意事項
fileを利用している場合にmoodledataがファイルロック出来ない場所にあるとセッション情報が保存されない（PHPスクリプトで排他ロックに失敗すると保存処理を中止する）ため、ログインできなくなる。
また、インストール中にconfig.phpが作成されるタイミングで記述するとインストールの継続が不可能になるため、インストール後に追記する必要がある。
Webサーバ1台+DBサーバ1台のような構成ならfileのセッション情報保存先を変更することで対応可能。Webサーバが複数台ある場合はセッション情報を共有するためにDBやMemcached、Redisを利用する。

#設定について
##設定変更の背景
Pound配下で複数のWebサーバを利用している環境でdatabaseをセッション管理に利用していたが、50名程度が同時利用するような状況でまれにslow queryが記録され、Webサーバの負荷に余裕があるにも関わらず利用者から速度低下が報告されていたため設定を見直すこととした。なお、DBサーバは高速なストレージを利用していた訳ではない（安物NASでiSCSIを利用）。

##設定方法
セッション情報保存先ごとの速度比較は行っていないが、MUCでも利用したいためredisを利用することにする。
DBサーバ(192.168.101.101)にRedisを導入してWebサーバ側からtelnetなどで疎通確認を行っておく。
Moodleのconfig.phpに以下の内容を記載して設定終了。詳しくはconfig.dist.phpを参照。

```php:config.php
$CFG->dbsessions = false;
$CFG->session_handler_class = '\core\session\redis';
$CFG->session_redis_host = '192.168.101.101';
$CFG->session_redis_port = 6379;
$CFG->session_redis_database = 0;
$CFG->session_redis_prefix = '';
$CFG->session_redis_acquire_lock_timeout = 120;
$CFG->session_redis_lock_expire = 7200;
```

冒頭の**$CFG->dbsessions**は本来不要だが、Moodleのサイト管理ページでDBセッションの有効化チェックが入れられないようにするため記載している。

Moodle 3.3以降であれば`# yum install -y php-pecl-igbinary`を実行してigbinaryを導入しておくと高速化が期待できる模様。

動作チェックはRedisサーバにtelnetで接続して**MONITOR**コマンドを実行するなどで行う。

## 上書きされるセッション関連設定
Moodle 3.4では[このあたり](https://github.com/moodle/moodle/blob/MOODLE_34_STABLE/lib/classes/session/manager.php#L252)でPHPの設定値が上書きされているため、php.iniなどで設定してもMoodle上では反映されないことになる。Moodle 3.1あたりでも同一内容の模様。

```php:lib/classes/session/manager.php
session_name($sessionname);
session_set_cookie_params(0, $CFG->sessioncookiepath, $CFG->sessioncookiedomain, $cookiesecure, $CFG->cookiehttponly);
ini_set('session.use_trans_sid', '0');
ini_set('session.use_only_cookies', '1');
ini_set('session.hash_function', '0');        // For now MD5 - we do not have room for sha-1 in sessions table.
ini_set('session.use_strict_mode', '0');      // We have custom protection in session init.
ini_set('session.serialize_handler', 'php');  // We can move to 'php_serialize' after we require PHP 5.5.4 form Moodle.

// Moodle does normal session timeouts, this is for leftovers only.
ini_set('session.gc_probability', 1);
ini_set('session.gc_divisor', 1000);
ini_set('session.gc_maxlifetime', 60*60*24*4);
```

##その後
同時アクセス数の増加に伴って（謎の？）速度低下が報告されることは無くなった。
というわけで複数のWebサーバでMoodleサイトを負荷分散している場合はDBではなくRedisを利用するのが良いと思う。