読者です 読者をやめる 読者になる 読者になる

【Node.js】MySQLのトランザクション対応

Node.js

NodeJSはシングルプロセスなので、
プールした接続だとトランザクションを管理できない。
トランザクションを開始する場合は新規の接続を生成する必要がある。
トランザクションを利用しない単発のINSERTであれば、プールした接続を使いまわしても大丈夫。


まずは app.js を書く。
トランザクション用のマスターDB用接続情報を app.set() する。
プール用の接続も app.set() する。

var mysql = require('mysql');
app.set('mysql', mysql);

//トランザクション用マスターの接続情報をセット。
//トランザクションを開始する際に接続するので、ここでは接続情報だけをセット。
var masterConf = {
  host     : 'localhost',
  user     : 'root',
  password : 'pass',
  database : 'db1',
  connectTimeout : 1000, //タイムアウト(msec)
  supportBigNumbers : true, //bigint, decimal をサポートする。
  connectionLimit : 10, //一度に生成する接続インスタンスの数
  removeNodeErrorCount : 3 //指定した回数以上接続エラーになったら接続選択対象から削除する。
};
app.set('masterConf', masterConf);

//プールする接続をセットする。
var slaveConf = {
  host     : 'localhost',
  user     : 'root',
  password : 'pass',
  database : 'db1',
  connectTimeout : 1000, //タイムアウト(msec)
  supportBigNumbers : true, //bigint, decimal をサポートする。
  connectionLimit : 10, //一度に生成する接続インスタンスの数
  removeNodeErrorCount : 3 //指定した回数以上接続エラーになったら接続選択対象から削除する。
};
var dbPool = mysql.createPoolCluster();
dbPool.add('MASTER', masterConf);
dbPool.add('SLAVE1', slaveConf);
dbPool.add('SLAVE2', slaveConf);
app.set('dbPool', dbPool);

以下はrouterになる。
まずはプール接続を利用する場合。
これは特に問題ない。

var app = req.app;
var dbPool = app.get("dbPool");

//Slave接続
var slave = dbPool.of('SLAVE*', 'RANDOM');
slave.getConnection(function(err, connection) {

    if(err){
		//失敗
		throw err;
    }

	//select
	var sql = 'SELECT * FROM tb1 WHERE id = ? OR id = ?';
	connection.query(sql, [1, 2], function(err, rows) {
		if (err) {
			throw err;
		}

		//結果
		console.log(rows);
		connection.release();
	});
});

//Master接続
var masterPool = dbPool.of('MASTER');
masterPool.getConnection(function(err, connection) {
    if(err){
		throw err;
    }

	var sql = 'INSERT INTO tb1 VALUES(?, ?)';
	connection.query(sql, [10, 'simple'], function(err, result) {
		if (err) {
			throw err;
		}
		console.log(result);

		connection.release();
	});
});

次はトランザクションを利用する場合。
ポイントは新規接続を確立している点。
これでトランザクションの整合性が保たれる。

//トランザクション
var app = req.app;
var mysql = app.get("mysql");

var master = mysql.createConnection(app.get("masterConf"));
master.connect(function(err) {
	//接続時のエラー
	if (err) {
		console.error(err);
		return;
	}
	console.log('connected master');
});

master.beginTransaction(function(err) {
	if (err) {
		console.error(err);
		return;
	}

	var sql = 'INSERT INTO tb1 VALUES(?, ?)';
	master.query(sql, [10, 'transaction_data'], function(err, result) {
		if (err) {
			master.rollback(function() {
				console.error(err);
				throw err;
    		});
		}

		master.commit(function(err) {
    		if (err) { 
    			master.rollback(function() {
    		  		throw err;
    			});
    		}
    		console.log('success!');
  		});

		res.send('END');

		console.log(result);
	});
});