diff --git a/documentation/Prepared-Statements.md b/documentation/Prepared-Statements.md index 72cb97bc35..2629ce3eef 100644 --- a/documentation/Prepared-Statements.md +++ b/documentation/Prepared-Statements.md @@ -32,3 +32,7 @@ connection.prepare('select ? + ? as tests', function(err, statement) { }); ``` Note that you should not use statement after connection reset (`changeUser()` or disconnect). Statement scope is connection, you need to prepare statement for each new connection in order to use it. + +# Configuration + +`maxPreparedStatements` : We keep the cached statements in a [lru-cache](https://github.com/isaacs/node-lru-cache). Default size is `16000` but you can use this option to override it. Any statements that are dropped from cache will be `closed`. diff --git a/lib/commands/change_user.js b/lib/commands/change_user.js index 7e56bed28a..9edadab4f8 100644 --- a/lib/commands/change_user.js +++ b/lib/commands/change_user.js @@ -37,7 +37,7 @@ ChangeUser.prototype.start = function (packet, connection) { this.currentConfig.database = this.database; this.currentConfig.charsetNumber = this.charsetNumber; // reset prepared statements cache as all statements become invalid after changeUser - connection._statements = {}; + connection._statements.reset(); connection.writePacket(packet.toPacket()); return ChangeUser.prototype.handshakeResult; }; diff --git a/lib/connection.js b/lib/connection.js index 5b99977d14..5dd1869edf 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -4,8 +4,8 @@ var Tls = require('tls'); var Timers = require('timers'); var EventEmitter = require('events').EventEmitter; var Queue = require('double-ended-queue'); - var SqlString = require('sqlstring'); +var LRU = require('lru-cache'); var PacketParser = require('./packet_parser.js'); var Packet = require('./packets/packet.js'); @@ -51,7 +51,10 @@ function Connection (opts) this._paused = false; this._paused_packets = new Queue(); - this._statements = {}; + this._statements = LRU({ + max: this.config.maxPreparedStatements, + dispose: function (key, statement) { statement.close(); } + }); // TODO: make it lru cache // https://github.com/mercadolibre/node-simple-lru-cache @@ -498,9 +501,9 @@ Connection.prototype.unprepare = function unprepare (sql) { options.sql = sql; } var key = statementKey(options); - var stmt = this._statements[key]; + var stmt = this._statements.get(key); if (stmt) { - this._statements[key] = null; + this._statements.del(key); stmt.close(); } return stmt; @@ -531,7 +534,7 @@ Connection.prototype.execute = function execute (sql, values, cb) { var connection = this; var key = statementKey(options); - var statement = connection._statements[key]; + var statement = connection._statements.get(key); options.statement = statement; var executeCommand = new Commands.Execute(options, cb); @@ -547,7 +550,7 @@ Connection.prototype.execute = function execute (sql, values, cb) { return; } executeCommand.statement = stmt; - connection._statements[key] = stmt; + connection._statements.set(key, stmt); connection.addCommand(executeCommand); }); } else { diff --git a/lib/connection_config.js b/lib/connection_config.js index 70cc25c23f..d36a43401e 100644 --- a/lib/connection_config.js +++ b/lib/connection_config.js @@ -70,6 +70,7 @@ function ConnectionConfig (options) { options.flags || ''); this.connectAttributes = options.connectAttributes; + this.maxPreparedStatements = options.maxPreparedStatements || 16000; } ConnectionConfig.mergeFlags = function (default_flags, user_flags) { diff --git a/package.json b/package.json index 2542e08315..b68b661204 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "double-ended-queue": "2.1.0-0", "iconv-lite": "^0.4.13", "long": "^3.2.0", + "lru-cache": "^4.0.1", "named-placeholders": "1.1.1", "object-assign": "^4.1.0", "readable-stream": "2.1.5", diff --git a/test/integration/connection/test-execute-cached.js b/test/integration/connection/test-execute-cached.js index 03e19203e3..09c10d6d27 100644 --- a/test/integration/connection/test-execute-cached.js +++ b/test/integration/connection/test-execute-cached.js @@ -24,9 +24,9 @@ connection.execute(q, [123], function (err, _rows, _fields) { throw err; } rows2 = _rows; - assert(Object.keys(connection._statements).length == 1); - assert(connection._statements[key].query == q); - assert(connection._statements[key].parameters.length == 1); + assert(connection._statements.length == 1); + assert(connection._statements.get(key).query == q); + assert(connection._statements.get(key).parameters.length == 1); connection.end(); }); }); @@ -38,4 +38,3 @@ process.on('exit', function () { assert.deepEqual(rows1, [{'test': 125}]); assert.deepEqual(rows2, [{'test': 126}]); }); -