Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Check in some initial work

  • Loading branch information...
commit 61b94ee0ab689327e25c8febf94cbae09a9ec3ce 0 parents
@felixge authored
125 benchmark/row-parsing/BufferCreator.js
@@ -0,0 +1,125 @@
+exports.create = function(options) {
+ var buffers = [];
+ var rows = 0;
+ var number = 0;
+
+ var buffer;
+ var rowBuffer;
+ var bufferOffset;
+ var rowBufferOffset;
+
+ while (rows < options.rowCount) {
+ if (!rowBuffer) {
+ rowBuffer = this.createRowBuffer(options.row, number);
+ rowBufferOffset = 0;
+ number++;
+ if (number > 255) number = 0;
+ }
+
+ if (!buffer) {
+ buffer = new Buffer(options.bufferSize);
+ bufferOffset = 0;
+ }
+
+ var rowBufferEnd = (buffer.length - bufferOffset < rowBuffer.length - rowBufferOffset)
+ ? rowBufferOffset + (buffer.length - bufferOffset)
+ : rowBuffer.length;
+
+ rowBuffer.copy(buffer, bufferOffset, rowBufferOffset, rowBufferEnd);
+
+ bufferOffset += rowBufferEnd - rowBufferOffset;
+ rowBufferOffset = rowBufferEnd;
+
+ if (bufferOffset === buffer.length) {
+ buffers.push(buffer);
+ buffer = null;
+ }
+
+ if (rowBufferOffset === rowBuffer.length) {
+ rowBuffer = null;
+ rows++;
+ }
+ }
+
+ if (bufferOffset > 0) {
+ buffers.push(buffer.slice(0, bufferOffset));
+ }
+
+ return buffers;
+};
+
+exports.createRowBuffer = function(row, number) {
+ var self = this;
+
+ var columns = Object
+ .keys(row)
+ .map(function(columnName) {
+ var column = row[columnName];
+ return (typeof column === 'function')
+ ? column() + ''
+ : column + '';
+ });
+
+ var length = columns.reduce(function(size, column) {
+ var columnLength = Buffer.byteLength(column, 'utf-8');;
+ return size + self.lengthCodedBinaryLength(columnLength) + columnLength;
+ }, 4);
+
+ var buffer = new Buffer(length);
+
+ columns.reduce(function(offset, column) {
+ var columnLength = Buffer.byteLength(column, 'utf-8');
+ offset = self.writeLengthCodedBinary(buffer, columnLength, offset);
+ buffer.write(column, offset, 'utf-8');
+ return offset + columnLength;
+ }, 4);
+
+ this.writeUnsignedInteger(buffer, length - 4, 3, 0);
+ this.writeUnsignedInteger(buffer, number, 1, 3);
+
+ return buffer;
+};
+
+exports.lengthCodedBinaryLength = function(value) {
+ if (value <= 250) {
+ return 1;
+ } else if (value <= Math.pow(2, 16)) {
+ return 3;
+ } else if (value <= Math.pow(2, 24)) {
+ return 4;
+ } else if (value <= Math.pow(2, 64)) {
+ return 9;
+ }
+
+ throw new Error('Not yet implemented');
+};
+
+exports.writeLengthCodedBinary = function(buffer, value, offset) {
+ if (value <= 250) {
+ buffer[offset] = value;
+ return offset + 1;
+ }
+
+ if (value <= Math.pow(2, 16)) {
+ buffer[offset] = 252;
+ buffer[offset + 1] = value & 0xff;
+ buffer[offset + 2] = (value >> 8) & 0xff;
+ return offset + 3;
+ }
+
+ buffer[offset + 3] = (value >> 16) & 0xff;
+
+ if (value <= Math.pow(2, 24)) {
+ // 24 Bit Marker
+ buffer[offset + 0] = 253;
+ return;
+ }
+
+ throw new Error('Not yet implemented');
+};
+
+exports.writeUnsignedInteger = function(buffer, value, length, offset) {
+ for (var i = 0; i < length; i++) {
+ buffer[i + offset] = (value >> (i * 8)) & 0xff;
+ }
+};
99 benchmark/row-parsing/run.js
@@ -0,0 +1,99 @@
+var Parser = require('../../lib/protocol/Parser');
+var BufferCreator = require('./BufferCreator');
+var rowCount = 100000;
+var bufferSize = 64 * 1024;
+var profiler = require('profiler');
+
+profiler.pause();
+
+var value = '';
+for (var i = 0; i < 256; i++) {
+ value += 'a';
+}
+
+var rowId = 0;
+var row = {
+ id: function() {
+ return rowId++;
+ },
+ some_field: value,
+};
+
+console.log(
+ '---> Generating %s rows inside %s kb buffers ...',
+ rowCount,
+ bufferSize / 1024
+);
+
+var start = Date.now();
+
+var buffers = BufferCreator.create({
+ rowCount : rowCount,
+ bufferSize : bufferSize,
+ row : row,
+});
+
+var duration = Date.now() - start;
+
+var length = buffers.reduce(function(total, buffer) {
+ return total + buffer.length;
+}, 0);
+
+console.log(
+ 'Generated %s buffers (%s mb) in %s ms.',
+ buffers.length,
+ (length / 1024 / 1024).toFixed(2),
+ duration
+);
+
+console.log('---> Parsing rows ...');
+
+var fieldPackets = [
+ {name: 'id', fieldType: Parser.FIELD_TYPE_LONG},
+ {name: 'some_field', fieldType: Parser.FIELD_TYPE_VARCHAR},
+];
+
+var parser = new Parser({
+ fieldPackets: fieldPackets,
+});
+
+profiler.resume();
+
+var start = Date.now();
+var repeat = 10;
+for (var i = 0; i < repeat; i++) {
+ buffers.forEach(function(buffer) {
+ parser.write(buffer);
+ });
+}
+
+rowCount = rowCount * repeat;
+length = length * repeat;
+
+var duration = Date.now() - start;
+var frequency = (rowCount / (duration / 1000));
+
+if (frequency > Math.pow(10, 6)) {
+ frequency = (frequency / Math.pow(10, 6)).toFixed(2) + ' Mhz';
+} else if (frequency > Math.pow(10, 3)) {
+ frequency = (frequency / Math.pow(10, 3)).toFixed(2) + ' Khz';
+} else {
+ frequency = (frequency) + ' Hz';
+}
+
+var throughput = (length / (duration / 1000));
+if (throughput > Math.pow(1024, 2)) {
+ throughput = (throughput / Math.pow(1024, 2)).toFixed(2) + ' Mb / sec';
+} else if (throughput > Math.pow(1024, 1)) {
+ throughput = (throughput / Math.pow(1024, 1)).toFixed(2) + ' Kb / sec';
+} else {
+ frequency = (frequency) + ' Byte / sec';
+}
+
+console.log(
+ 'Parsed %s rows in %s ms (%s / %s)',
+ rowCount,
+ duration,
+ frequency,
+ throughput
+);
10 index.js
@@ -0,0 +1,10 @@
+var MysqlClient = require('./lib/MysqlClient');
+var MysqlConnection = require('./lib/MysqlConnection');
+
+exports.createClient = function(config) {
+ return new MysqlClient({config: config});
+};
+
+exports.createConnection = function(config) {
+ return new MysqlConnection({config: config});
+};
17 lib/MysqlClient.js
@@ -0,0 +1,17 @@
+//var MysqlClientConfig = require('./MysqlClientConfig');
+//var MysqlConnection = require('./MysqlConnection');
+
+//module.exports = MysqlClient;
+//function MysqlClient(options) {
+ //this.config = new MysqlClientConfig(options.config);
+ //this.connection = null;
+//}
+
+//MysqlClient.prototype.connect = function(cb) {
+ //this.connection = new MysqlConnection({config: this.config});
+ //this.connection.connect(cb);
+//};
+
+//MysqlClient.prototype.query = function(sql, cb) {
+
+//};
7 lib/MysqlConfig.js
@@ -0,0 +1,7 @@
+module.exports = MysqlClientConfig;
+function MysqlClientConfig(options) {
+ this.host = options.host || 'localhost';
+ this.user = options.user || undefined;
+ this.password = options.password || undefined;
+ this.port = options.port || 3306;
+}
22 lib/MysqlConnection.js
@@ -0,0 +1,22 @@
+var Net = require('net');
+var MysqlConfig = require('./MysqlConfig');
+var Parser = require('./protocol/Parser');
+
+module.exports = MysqlConnection;
+function MysqlConnection(options) {
+ this.config = new MysqlConfig(options.config);
+ this.parser = new Parser();
+ this.socket = options.socket;
+}
+
+MysqlConnection.prototype.connect = function(cb) {
+ if (!this.socket) {
+ this.socket = Net.createConnection(this.config.port, this.config.host);
+ }
+
+ var self = this;
+
+ this.socket.on('data', function(buffer) {
+ self.parser.write(buffer);
+ });
+};
5 lib/protocol/PacketHeader.js
@@ -0,0 +1,5 @@
+module.exports = PacketHeader;
+function PacketHeader(length, number) {
+ this.length = length;
+ this.number = number;
+}
158 lib/protocol/Parser.js
@@ -0,0 +1,158 @@
+var PacketHeader = require('./PacketHeader');
+
+module.exports = Parser;
+function Parser(options) {
+ this._buffer = null;
+ this._offset = 0;
+
+ this._packetHeader = null;
+}
+
+Parser.prototype.write = function(buffer) {
+ this._append(buffer);
+
+ var self = this;
+ var packetHeader = this._packetHeader;
+
+ while (true) {
+ if (!packetHeader) {
+ if (this._bytesRemaining() < 4) {
+ break;
+ }
+
+ packetHeader = new PacketHeader(
+ this.readUnsignedNumber(3),
+ this.readUnsignedNumber(1)
+ );
+ }
+
+ if (this._bytesRemaining() < packetHeader.length) {
+ break;
+ }
+
+ //this._offset += packetHeader.length;
+
+ var id = this.readLengthCodedString();
+ var name = this.readLengthCodedString();
+
+ //console.log(packetHeader);
+
+ packetHeader = null;
+ }
+
+ this._packetHeader = packetHeader;
+};
+
+Parser.prototype._bytesRemaining = function() {
+ return this._buffer.length - this._offset;
+};
+
+Parser.prototype._append = function(newBuffer) {
+ var oldBuffer = this._buffer;
+ if (!oldBuffer) {
+ this._buffer = newBuffer;
+ return;
+ }
+
+ var bytesRemaining = this._bytesRemaining();
+ var newLength = bytesRemaining + newBuffer.length;
+
+ var combinedBuffer = (newLength < this._offset)
+ ? oldBuffer.slice(0, newLength)
+ : new Buffer(newLength);
+
+ oldBuffer.copy(combinedBuffer, 0, this._offset);
+ newBuffer.copy(combinedBuffer, bytesRemaining);
+
+ this._buffer = combinedBuffer;
+ this._offset = 0;
+};
+
+Parser.prototype.readUnsignedNumber = function(bytes) {
+ var bytesRead = 0;
+ var value = 0;
+
+ while (bytesRead < bytes) {
+ var byte = this._buffer[this._offset + bytesRead];
+
+ value += byte * Math.pow(256, bytesRead);
+
+ bytesRead++;
+ }
+
+ this._offset += bytesRead;
+
+ return value;
+};
+
+Parser.prototype.readLengthCodedString = function() {
+ var length = this.readLengthCodedBinary();
+ return this.readString(length);
+};
+
+Parser.prototype.readLengthCodedBinary = function() {
+ var byte = this._buffer[this._offset];
+ this._offset++;
+
+ if (byte <= 251) {
+ return (byte === 251)
+ ? null
+ : byte;
+ }
+
+ if (byte === 252) {
+ var length = 2;
+ } else {
+ throw new Error('not implemented');
+ }
+
+ var value = 0;
+ for (var bytesRead = 0; bytesRead < length; bytesRead++) {
+ var byte = this._buffer[this._offset + bytesRead];
+ value += Math.pow(256, bytesRead) * byte;
+ }
+
+ this._offset += bytesRead;
+ return value;
+};
+
+Parser.prototype.readString = function(length) {
+ var end = this._offset + length;
+ var value = this._buffer.toString('utf-8', this._offset, end);
+
+ this._offset = end;
+ return value;
+};
+
+Parser.prototype.advance = function(bytes) {
+ this._offset += bytes;
+};
+
+
+Parser.FIELD_TYPE_DECIMAL = 0x00;
+Parser.FIELD_TYPE_TINY = 0x01;
+Parser.FIELD_TYPE_SHORT = 0x02;
+Parser.FIELD_TYPE_LONG = 0x03;
+Parser.FIELD_TYPE_FLOAT = 0x04;
+Parser.FIELD_TYPE_DOUBLE = 0x05;
+Parser.FIELD_TYPE_NULL = 0x06;
+Parser.FIELD_TYPE_TIMESTAMP = 0x07;
+Parser.FIELD_TYPE_LONGLONG = 0x08;
+Parser.FIELD_TYPE_INT24 = 0x09;
+Parser.FIELD_TYPE_DATE = 0x0a;
+Parser.FIELD_TYPE_TIME = 0x0b;
+Parser.FIELD_TYPE_DATETIME = 0x0c;
+Parser.FIELD_TYPE_YEAR = 0x0d;
+Parser.FIELD_TYPE_NEWDATE = 0x0e;
+Parser.FIELD_TYPE_VARCHAR = 0x0f;
+Parser.FIELD_TYPE_BIT = 0x10;
+Parser.FIELD_TYPE_NEWDECIMAL = 0xf6;
+Parser.FIELD_TYPE_ENUM = 0xf7;
+Parser.FIELD_TYPE_SET = 0xf8;
+Parser.FIELD_TYPE_TINY_BLOB = 0xf9;
+Parser.FIELD_TYPE_MEDIUM_BLOB = 0xfa;
+Parser.FIELD_TYPE_LONG_BLOB = 0xfb;
+Parser.FIELD_TYPE_BLOB = 0xfc;
+Parser.FIELD_TYPE_VAR_STRING = 0xfd;
+Parser.FIELD_TYPE_STRING = 0xfe;
+Parser.FIELD_TYPE_GEOMETRY = 0xff;
21 package.json
@@ -0,0 +1,21 @@
+{
+ "author": "Felix Geisendörfer <felix@debuggable.com> (http://debuggable.com/)",
+ "name": "mysql",
+ "description": "A new version of my mysql client.",
+ "version": "0.0.0",
+ "repository": {
+ "url": ""
+ },
+ "main": "./index",
+ "scripts": {
+ "test": "make test"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "dependencies": {
+ "underscore": "~1.3.1"
+ },
+ "devDependencies": {},
+ "optionalDependencies": {}
+}
11 test/common.js
@@ -0,0 +1,11 @@
+var Mysql = require('../');
+var common = exports;
+
+common.createConnection = function() {
+ return Mysql.createConnection({
+ host : process.env.MYSQL_HOST,
+ port : process.env.MYSQL_PORT,
+ user : process.env.MYSQL_USER,
+ password : process.env.MYSQL_PASSWORD,
+ });
+};
21 test/integration/test-simple-query.js
@@ -0,0 +1,21 @@
+var common = require('../common');
+var connection = common.createConnection();
+var assert = require('assert');
+var rows = undefined;
+
+connection.connect(function(err) {
+ if (err) throw err;
+
+ connection.query('SELECT 1 = 1', function(err, _rows) {
+ if (err) throw err;
+
+ rows = _rows;
+
+ client.end();
+ });
+});
+
+process.on('exit', function() {
+ assert.deepEqual(rows, [{1: 1}]);
+});
+
Please sign in to comment.
Something went wrong with that request. Please try again.