a2 Tech blog

試したこと・調べたこと・感じたことを発信するITエンジニアの日記です。仕事とは直接関係ないけど興味あることを模索していきます。

node-mysqlで接続が切れる点を改善

f:id:ninna2:20170409135157j:plain:w360

前回(Heroku/mysql/Node.jsをiPhoneだけで構築する)、herokuでMySQLを構築してNode.jsで接続しましたが、Node.js側の実装が少しイケてなかったので修正します。最初はうまくMySQLに接続出来ていたので問題ないかなと思っていたのですが、しばらく放置した後に接続してみたら繋がらなくなりました。

前回記事は下記です。

ninna2.hatenablog.com

エラー内容

ログを見ると下記のように出てました。

app[web.1]: events.js:160
app[web.1]:       throw er; // Unhandled 'error' event
app[web.1]:       ^
app[web.1]: 
app[web.1]: Error: Connection lost: The server closed the connection.
app[web.1]:     at Protocol.end (/app/node_modules/mysql/lib/protocol/Protocol.js:109:13)
app[web.1]:     at Socket.<anonymous> (/app/node_modules/mysql/lib/Connection.js:109:28)
app[web.1]:     at emitNone (events.js:91:20)
app[web.1]:     at Socket.emit (events.js:185:7)
app[web.1]:     at endReadableNT (_stream_readable.js:974:12)
app[web.1]:     at _combinedTickCallback (internal/process/next_tick.js:74:11)
app[web.1]:     at process._tickCallback (internal/process/next_tick.js:98:9)
heroku[web.1]: State changed from up to crashed
heroku[web.1]: Process exited with status 1

“Error: Connection lost” ですって・・・connectionが切れてる。

少し調べて見ると、MySQLのセキュリティの仕様上、定期的にデータベースとの接続が切れるようです。接続が切れた後に適切にエラーハンドリングしておかないとherokuがクラッシュしてしまっていました。もう少ししっかりコーディングしないといけなかったですね。

反省です。

コネクションが切れた際に再接続する

コネクションが切れることを前提にしてプログラムを組んでいく方法です。下記のGithubのコードを参考にしました。

github.com

‘PROTOCOL_CONNECTION_LOST’ の時に、再度コネクションを取得するように実装されています。これで、コネクションが切れても再接続に行くのでクラッシュすることはなくなります。

var express = require('express');
var mysql = require('mysql2');
var app = express();

var db_config = {
    host: '',
    user: '',
    password: '',
    database: ''
};

app.set('port', (process.env.PORT || 5000));

var connection;

function handleDisconnect() {
    console.log('INFO.CONNECTION_DB: ');
    connection = mysql.createConnection(db_config);
    
    //connection取得
    connection.connect(function(err) {
        if (err) {
            console.log('ERROR.CONNECTION_DB: ', err);
            setTimeout(handleDisconnect, 1000);
        }
    });
    
    //error('PROTOCOL_CONNECTION_LOST')時に再接続
    connection.on('error', function(err) {
        console.log('ERROR.DB: ', err);
        if (err.code === 'PROTOCOL_CONNECTION_LOST') {
            console.log('ERROR.CONNECTION_LOST: ', err);
            handleDisconnect();
        } else {
            throw err;
        }
    });
}

handleDisconnect();

app.get('/', function(request, response) {
    connection.query('SELECT * FROM t_message WHERE id=1', function(err, rows, fields){
        if(err){
            console.log('ERROR.SELECT_DB: ', err);
            throw err;
        }
        response.writeHead(200,{'Content-Type': 'text/plain'});
        response.write(rows[0].message);
        response.end();
    });
});

app.listen(app.get('port'), function() {
    console.log('heroku-mysql app is running on port', app.get('port'));
});

ConnectionPoolを使う

そもそも、ConnectionPool 使えばいいじゃんという考えで組み直しました。こっちの方がシンプルですね。詳しくはまだ調べていないのですが、Poolした接続だとトランザクションが使えないみたい。シングルスレッドだから?調べないといけないですね・・・今回はトランザクション関係ないので、普通にPool使います。

createConnectionの代わりに、createPool を使います。

var pool = mysql.createPool(db_config);

pool.getConnectionでconnectionを取得して使います。使い終わったら release します。これでconnectionが切れることはないはずです。

   pool.getConnection(function(err, connection){
        connection.query('SELECT...', function(err, rows, fields){
            //処理
            connection.release();
            });
        });

全体像を載せておきます。

var express = require('express');
var mysql = require('mysql2');
var app = express();

var db_config = {
    host: '',
    user: '',
    password: '',
    database: ''
};

var pool = mysql.createPool(db_config);

app.set('port', (process.env.PORT || 5000));

app.get('/', function(request, response) {
    console.log("heroku-mysql!!");
    pool.getConnection(function(err, connection){
        connection.query('SELECT * FROM t_message WHERE id=1', function(err, rows, fields){
        if(err){
            console.log('error: ', err);
            throw err;
        }
        response.writeHead(200,{'Content-Type': 'text/plain'});
        response.write(rows[0].message);
        response.end();
        connection.release();
        });
    });
});

app.listen(app.get('port'), function() {
  console.log('heroku-mysql app is running on port', app.get('port'));
});

おまけ

実は、node-mysqlから node-mysql2 に変更してます。パフォーマンスがこちらの方が良いみたいです。使い方は変わらないようです。

github.com

やはりちゃんと実装をしないといけないですね。身にしみて感じました。