diff --git a/index.js b/index.js index 352d190d86..6b48b500c9 100644 --- a/index.js +++ b/index.js @@ -2,6 +2,7 @@ var SqlString = require('sqlstring'); var Connection = require('./lib/connection.js'); var ConnectionConfig = require('./lib/connection_config.js'); +var parserCache = require("./lib/parsers/parser_cache"); module.exports.createConnection = function(opts) { return new Connection({ config: new ConnectionConfig(opts) }); @@ -61,3 +62,11 @@ exports.__defineGetter__('Charsets', function() { exports.__defineGetter__('CharsetToEncoding', function() { return require('./lib/constants/charset_encodings.js'); }); + +exports.setMaxParserCache = function (max) { + parserCache.setMaxCache(max); +}; + +exports.clearParserCache = function () { + parserCache.clearCache(); +}; diff --git a/lib/commands/execute.js b/lib/commands/execute.js index 1954a5e402..abb78e1d0c 100644 --- a/lib/commands/execute.js +++ b/lib/commands/execute.js @@ -6,7 +6,7 @@ var Packets = require('../packets/index.js'); var objectAssign = require('object-assign'); -var compileParser = require('../compile_binary_parser.js'); +var getBinaryParser = require('../parsers/binary_parser.js'); function Execute(options, callback) { Command.call(this); @@ -33,13 +33,7 @@ function Execute(options, callback) { util.inherits(Execute, Command); Execute.prototype.buildParserFromFields = function(fields, connection) { - var parserKey = connection.keyFromFields(fields, this.options); - var parser = connection.binaryProtocolParsers[parserKey]; - if (!parser) { - parser = compileParser(fields, this.options, connection.config); - connection.binaryProtocolParsers[parserKey] = parser; - } - return parser; + return getBinaryParser(fields, this.options, connection.config); }; Execute.prototype.start = function(packet, connection) { diff --git a/lib/commands/query.js b/lib/commands/query.js index 20d8fbf10c..cfd0251c28 100644 --- a/lib/commands/query.js +++ b/lib/commands/query.js @@ -7,7 +7,7 @@ var objectAssign = require('object-assign'); var Command = require('./command.js'); var Packets = require('../packets/index.js'); -var compileParser = require('../compile_text_parser.js'); +var getTextParser = require('../parsers/text_parser.js'); var ServerStatus = require('../constants/server_status.js'); var CharsetToEncoding = require('../constants/charset_encodings.js'); @@ -195,15 +195,10 @@ Query.prototype.readField = function(packet, connection) { } // last field received - if (this._receivedFieldsCount == this._fieldCount) { + if (this._receivedFieldsCount === this._fieldCount) { var fields = this._fields[this._resultIndex]; this.emit('fields', fields); - var parserKey = connection.keyFromFields(fields, this.options); - this._rowParser = connection.textProtocolParsers[parserKey]; - if (!this._rowParser) { - this._rowParser = compileParser(fields, this.options, connection.config); - connection.textProtocolParsers[parserKey] = this._rowParser; - } + this._rowParser = getTextParser(fields, this.options, connection.config); return Query.prototype.fieldsEOF; } return Query.prototype.readField; diff --git a/lib/connection.js b/lib/connection.js index 69ad5cbcd3..66ba6b50df 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -58,19 +58,6 @@ function Connection(opts) { } }); - // TODO: make it lru cache - // https://github.com/mercadolibre/node-simple-lru-cache - // or https://github.com/rsms/js-lru - // or https://github.com/monsur/jscache - // or https://github.com/isaacs/node-lru-cache - // - // key is field.name + ':' + field.columnType + ':' field.flags + '/' - this.textProtocolParsers = {}; - - // TODO: not sure if cache should be separate (same key as with textProtocolParsers) - // or part of prepared statements cache (key is sql query) - this.binaryProtocolParsers = {}; - this.serverCapabilityFlags = 0; this.authorized = false; @@ -639,25 +626,6 @@ Connection.prototype.resume = function resume() { this.stream.resume(); }; -Connection.prototype.keyFromFields = function keyFromFields(fields, options) { - var res = - typeof options.nestTables + - '/' + - options.nestTables + - '/' + - options.rowsAsArray + - options.supportBigNumbers + - '/' + - options.bigNumberStrings + - '/' + - typeof options.typeCast; - for (var i = 0; i < fields.length; ++i) { - res += - '/' + fields[i].name + ':' + fields[i].columnType + ':' + fields[i].flags; - } - return res; -}; - Connection.statementKey = function(options) { return ( typeof options.nestTables + diff --git a/lib/compile_binary_parser.js b/lib/parsers/binary_parser.js similarity index 92% rename from lib/compile_binary_parser.js rename to lib/parsers/binary_parser.js index 0a46685c6f..f4e0bd4d1e 100644 --- a/lib/compile_binary_parser.js +++ b/lib/parsers/binary_parser.js @@ -1,10 +1,10 @@ -var FieldFlags = require('./constants/field_flags.js'); -var Charsets = require('./constants/charsets.js'); -var CharsetToEncoding = require('./constants/charset_encodings.js'); -var Types = require('./constants/types.js'); -var srcEscape = require('./helpers').srcEscape; +var FieldFlags = require('../constants/field_flags.js'); +var Charsets = require('../constants/charsets.js'); +var CharsetToEncoding = require('../constants/charset_encodings.js'); +var Types = require('../constants/types.js'); +var srcEscape = require('../helpers').srcEscape; var genFunc = require('generate-function'); - +var parserCache = require('./parser_cache.js'); var typeNames = []; for (var t in Types) { typeNames[Types[t]] = t; @@ -181,4 +181,8 @@ function readCodeFor(field, config, options, fieldNum) { } } -module.exports = compile; +function getBinaryParser(fields, options, config) { + return parserCache.getParser('binary', fields, options, config, compile); +} + +module.exports = getBinaryParser; diff --git a/lib/parsers/parser_cache.js b/lib/parsers/parser_cache.js new file mode 100644 index 0000000000..c148b5f978 --- /dev/null +++ b/lib/parsers/parser_cache.js @@ -0,0 +1,53 @@ +var LRU = require('lru-cache'); + +var parserCache = new LRU({ + max: 15000 +}); + +function keyFromFields(type, fields, options) { + var res = + type + + '/' + + typeof options.nestTables + + '/' + + options.nestTables + + '/' + + options.rowsAsArray + + options.supportBigNumbers + + '/' + + options.bigNumberStrings + + '/' + + typeof options.typeCast; + for (var i = 0; i < fields.length; ++i) { + res += + '/' + fields[i].name + ':' + fields[i].columnType + ':' + fields[i].flags; + } + return res; +} + +function getParser(type, fields, options, config, compiler) { + var key = keyFromFields(type, fields, options); + var parser = parserCache.get(key); + + if (parser) { + return parser; + } + + parser = compiler(fields, options, config); + parserCache.set(key, parser); + return parser; +} + +function setMaxCache(max) { + parserCache.max = max; +} + +function clearCache() { + parserCache.reset(); +} + +module.exports = { + getParser: getParser, + setMaxCache: setMaxCache, + clearCache: clearCache +}; diff --git a/lib/compile_text_parser.js b/lib/parsers/text_parser.js similarity index 93% rename from lib/compile_text_parser.js rename to lib/parsers/text_parser.js index 6e2a2ac25d..48f18d16b3 100644 --- a/lib/compile_text_parser.js +++ b/lib/parsers/text_parser.js @@ -1,8 +1,9 @@ -var Types = require('./constants/types.js'); -var Charsets = require('./constants/charsets.js'); -var CharsetToEncoding = require('./constants/charset_encodings.js'); -var srcEscape = require('./helpers').srcEscape; +var Types = require('../constants/types.js'); +var Charsets = require('../constants/charsets.js'); +var CharsetToEncoding = require('../constants/charset_encodings.js'); +var srcEscape = require('../helpers').srcEscape; var genFunc = require('generate-function'); +var parserCache = require('./parser_cache.js'); var typeNames = []; for (var t in Types) { @@ -189,4 +190,7 @@ function readCodeFor(type, charset, encodingExpr, config, options) { } } -module.exports = compile; +function getTextParser(fields, options, config) { + return parserCache.getParser('text', fields, options, config, compile); +} +module.exports = getTextParser;