セッション管理機能を Express にて実装するためのミドルウェアとして express-session がある。
こいつを用いると、セッションデータを保存しておくデータベースには様々なものを選ぶことができる。セッションデータを保存しておくデータベースとして、僕は CouchDB を利用している。
CouchDB を用いるためには、https://github.com/expressjs/session#session-store-implementation を参考にして、API を実装する必要があるが、connect-couchdb というパッケージを利用すると、自分で実装しなくていいから便利である。
express-session と connect-couchdb を組み合わせてセッション管理を実装していると、Error: Document update conflict
というエラーに出くわした。
数時間、こいつに悩まされていたのであるが、原因が分かったのでメモしておく。
以下の Sample code は、Error: Document update conflict
が必ず発生した。
(少なくとも僕の環境では)
Sample code
app.js
var express = require('express');
var path = require('path');
var morgan = require('morgan');
var http = require("http");
var session = require('express-session');
var ConnectCouchDB = new require("connect-couchdb")(session);
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
// uncomment after placing your favicon in /public
app.use(morgan('dev'));
app.use(express.static(path.join(__dirname, 'public')));
app.use("/", session ({
name: "test.sid",
secret: "test",
rolling: false,
cookie: {
httpOnly: true,
secure: false,
},
resave: true,
proxy: undefined,
saveUninitialized: true,
unset: "keep",
store: new ConnectCouchDB ({
name: "test_sessions",
username: "",
password: "",
host: "localhost",
port: "5984"
})
}));
app.get ("/", function (req, res) {
console.log(JSON.stringify(req.session));
req.session.a = Math.random();
req.session.save(callbackSave);
req.session.b = Math.random();
req.session.save(callbackSave);
res.send("hello world");
});
function callbackSave (error) {
if (error) {
throw error;
}
}
module.exports = app;
エラー原因の推測
// 1. リクエストを受け取ったら、クッキーを取得する。
// 2. クッキーに書かれている session id に該当するドキュメントを CouchDB から取得する。
req.session 変数に取得したドキュメントの内容が書き込まれる。
app.get ("/", function (req, res) {
console.log(JSON.stringify(req.session));
req.session.a = Math.random();
// 3. req.session.a が更新された。これを CouchDB へ書き込む。
// ここではエラーは発生しない。
req.session.save(callbackSave);
req.session.b = Math.random();
// 4. req.session.b が更新された。これを CouchDB へ書き込む。
// ここでエラーが発生する。
req.session.save(callbackSave);
res.send("hello world");
});
詳細なことはソースコードを読まなければ分からないが、2 の時点での CouchDB のドキュメントのリビジョン番号を 100 とする。3 の時点で CouchDB のドキュメントのリビジョン番号が 100 から 101 に更新される。4 の時点でも CouchDB のドキュメントのリビジョン番号を 100 から 101 に更新しようとするが、3 の時点において、既にドキュメントのリビジョン番号が 101 に更新されてしまうため、Conflict となるのだろう。
詳細な調査はしないが、express-session と connect-session を併用する場合には、用法を間違えないことである。