From ab157fe0f7d12626186b7e0952c3936684316637 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eneko=20Rodr=C3=ADguez?= Date: Fri, 24 May 2024 20:20:57 +0200 Subject: [PATCH 01/13] init changes --- lib/instrumentation/index.js | 2 + lib/instrumentation/modules/mariadb.js | 242 +++++++ package-lock.json | 77 ++- package.json | 1 + test/docker-compose.yml | 16 + .../instrumentation/modules/mariadb/_utils.js | 38 ++ .../modules/mariadb/mariadb.test.js | 640 ++++++++++++++++++ .../modules/mariadb/minitest-cb.test.js | 64 ++ .../modules/mariadb/minitest-cluster.test.js | 50 ++ .../modules/mariadb/minitest.test.js | 53 ++ .../modules/mariadb/pool-release-1.test.js | 64 ++ 11 files changed, 1241 insertions(+), 6 deletions(-) create mode 100644 lib/instrumentation/modules/mariadb.js create mode 100644 test/instrumentation/modules/mariadb/_utils.js create mode 100644 test/instrumentation/modules/mariadb/mariadb.test.js create mode 100644 test/instrumentation/modules/mariadb/minitest-cb.test.js create mode 100644 test/instrumentation/modules/mariadb/minitest-cluster.test.js create mode 100644 test/instrumentation/modules/mariadb/minitest.test.js create mode 100644 test/instrumentation/modules/mariadb/pool-release-1.test.js diff --git a/lib/instrumentation/index.js b/lib/instrumentation/index.js index 8af059cff23..1fdeb12720a 100644 --- a/lib/instrumentation/index.js +++ b/lib/instrumentation/index.js @@ -99,6 +99,8 @@ var MODULE_PATCHERS = [ }, { modPath: 'mysql' }, { modPath: 'mysql2' }, + { modPath: 'mariadb' }, + { modPath: 'mariadb/callback', patcher: './modules/mariadb.js' }, { modPath: 'next' }, { modPath: 'next/dist/server/api-utils/node.js' }, { modPath: 'next/dist/server/dev/next-dev-server.js' }, diff --git a/lib/instrumentation/modules/mariadb.js b/lib/instrumentation/modules/mariadb.js new file mode 100644 index 00000000000..d36c3f722fc --- /dev/null +++ b/lib/instrumentation/modules/mariadb.js @@ -0,0 +1,242 @@ +/* + * Copyright Elasticsearch B.V. and other contributors where applicable. + * Licensed under the BSD 2-Clause License; you may not use this file except in + * compliance with the BSD 2-Clause License. + */ + +'use strict'; + +var semver = require('semver'); +var sqlSummary = require('sql-summary'); + +var shimmer = require('../shimmer'); +var symbols = require('../../symbols'); +var { getDBDestination } = require('../context'); +const { config } = require('bluebird'); + +module.exports = function (mariadb, agent, { version, enabled, name }) { + console.log('Starting mariadb instrumentation'); + if (!enabled) { + return mariadb; + } + + if (!semver.satisfies(version, '>=3.3.0')) { + agent.logger.debug( + 'mariab version %s not supported - aborting...', + version, + ); + return mariadb; + } + + var ins = agent._instrumentation; + let defaultConfig = mariadb.defaultOptions(); + let config = {}; + + shimmer.wrap(mariadb, 'createConnection', wrapConnection); + shimmer.wrap(mariadb, 'createPool', wrapPool); + shimmer.wrap(mariadb, 'createPoolCluster', wrapPool); + + return mariadb; + + function wrapPool(original) { + return function wrappedPool() { + console.log('Inserting wrapPool'); + let result = original.apply(this, arguments); + shimmer.wrap( + result, + 'getConnection', + function wrapGetConnection(...args) { + console.log('Test'); + if (typeof arguments[0] === 'object') { + console.log('Cambiadno'); + config = { + ...defaultConfig, + ...arguments[0], + }; + } + + return wrapConnection.apply(result, args); + }, + ); + + if (typeof result.add === 'function') { + shimmer.wrap(result, 'add', wrapAdd); + shimmer.wrap(result, 'of', wrapPool); + } + + if (typeof result.query === 'function') { + shimmer.wrap(result, 'query', wrapQuery); + } + if (typeof result.execute === 'function') { + shimmer.wrap(result, 'execute', wrapQuery); + } + + if (typeof result.queryStream === 'function') { + shimmer.wrap(result, 'queryStream', wrapQuery); + } + return result; + }; + } + + function wrapAdd(original) { + return function wrappedAdd() { + console.log('Executing wrapAdd', original, arguments); + config = { + ...defaultConfig, + ...arguments[1], + }; + return original.apply(this, arguments); + }; + } + + function wrapConnection(original) { + return function wrappedConnection() { + console.log('Executing wrapConnection', name, arguments); + + if (typeof arguments[0] === 'object') { + config = { + ...defaultConfig, + ...arguments[0], + }; + } + console.log(arguments); + + if (!name.includes('callback')) { + console.log('Shimming promise'); + return original.apply(this, arguments).then((res) => { + shimmer.wrap(res, 'query', wrapQuery); + shimmer.wrap(res, 'execute', wrapQuery); + shimmer.wrap(res, 'queryStream', wrapQuery); + + return Promise.resolve(res); + }); + } + console.log('test'); + + if (typeof arguments[0] === 'function') { + console.log('Shimming callback'); + return original.apply(this, [ + (err, conn) => { + console.log('MI CALLBACK'); + console.log(err); + if (err) return arguments[0](err); + console.log('test345'); + shimmer.wrap(conn, 'query', wrapQuery); + shimmer.wrap(conn, 'execute', wrapQuery); + // shimmer.wrap(conn, 'queryStream', wrapQuery); + + return arguments[0](err, conn); + }, + ]); + } else { + console.log('Shimming WITHOUT callback'); + + let result = original.apply(this, arguments); + shimmer.wrap(result, 'query', wrapQuery); + shimmer.wrap(result, 'execute', wrapQuery); + // shimmer.wrap(conn, 'queryStream', wrapQuery); + + return result; + } + }; + } + function wrapQuery(original) { + console.log('Inserting wrapQuery', original); + return function wrappedQuery(sql, values, cb) { + console.log('Executing wrapQuery', arguments, config); + + agent.logger.debug('intercepted call to mariadb.%s', original.name); + var span = ins.createSpan(null, 'db', 'mariadb', 'query', { + exitSpan: true, + }); + if (!span) { + return original.apply(this, arguments); + } + + let hasCallback = false; + const wrapCallback = function (origCallback) { + hasCallback = true; + return ins.bindFunction(function wrappedCallback(_err) { + console.log('CAllbkac wrappeed', arguments); + span.end(); + return origCallback.apply(this, arguments); + }); + }; + let host, port, user, database; + if (typeof config === 'object') { + ({ host, port, user, database } = config); + } + + span._setDestinationContext(getDBDestination(host, port)); + let sqlStr; + switch (typeof sql) { + case 'string': + sqlStr = sql; + break; + case 'object': + sqlStr = sql.sql; + break; + case 'function': + arguments[0] = wrapCallback(sql); + break; + } + + if (sqlStr) { + span.setDbContext({ + type: 'sql', + instance: database, + user, + statement: sqlStr, + }); + span.name = sqlSummary(sqlStr); + } else { + span.setDbContext({ type: 'sql', instance: database, user }); + } + + if (typeof values === 'function') { + arguments[1] = wrapCallback(values); + } else if (typeof cb === 'function') { + arguments[2] = wrapCallback(cb); + } + + const spanRunContext = ins.currRunContext().enterSpan(span); + const result = ins.withRunContext( + spanRunContext, + original, + this, + ...arguments, + ); + + if (result && !hasCallback) { + ins.bindEmitter(result); + console.log('EMITER'); + shimmer.wrap(result, 'emit', function (origEmit) { + return function (event) { + console.log('Evento de callback', event); + switch (event) { + case 'error': + case 'close': + case 'send_end': + span.end(); + } + return origEmit.apply(this, arguments); + }; + }); + } + + if (!hasCallback) { + console.log('Ejecuting promise'); + return new Promise(async (resolve, reject) => { + console.log('test1'); + let awaitedResult = await original.apply(this, arguments); + console.log('test2'); + span.end(); + return resolve(awaitedResult); + }); + } + console.log('Devolviendo normal sin '); + console.log(arguments); + return original.apply(this, arguments); + }; + } +}; diff --git a/package-lock.json b/package-lock.json index c63d6db01e0..4b0e507f08c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -103,6 +103,7 @@ "koa-bodyparser": "^4.3.0", "koa-router": "^12.0.0", "lambda-local": "^2.0.2", + "mariadb": "^3.3.0", "memcached": "^2.2.2", "mimic-response": "1.0.0", "mkdirp": "^3.0.1", @@ -6534,6 +6535,12 @@ "@types/range-parser": "*" } }, + "node_modules/@types/geojson": { + "version": "7946.0.14", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz", + "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==", + "dev": true + }, "node_modules/@types/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", @@ -13242,9 +13249,9 @@ } }, "node_modules/lru-cache": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz", - "integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", + "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", "engines": { "node": "14 || >=16.14" } @@ -13276,6 +13283,34 @@ "resolved": "https://registry.npmjs.org/mapcap/-/mapcap-1.0.0.tgz", "integrity": "sha512-KcNlZSlFPx+r1jYZmxEbTVymG+dIctf10WmWkuhrhrblM+KMoF77HelwihL5cxYlORye79KoR4IlOOk99lUJ0g==" }, + "node_modules/mariadb": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/mariadb/-/mariadb-3.3.0.tgz", + "integrity": "sha512-sAL4bJgbfCAtXcE8bXI+NAMzVaPNkIU8hRZUXYfgNFoWB9U57G3XQiMeCx/A6IrS6y7kGwBLylrwgsZQ8kUYlw==", + "dev": true, + "dependencies": { + "@types/geojson": "^7946.0.14", + "@types/node": "^20.11.17", + "denque": "^2.1.0", + "iconv-lite": "^0.6.3", + "lru-cache": "^10.2.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/mariadb/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/measured-core": { "version": "1.51.1", "resolved": "https://registry.npmjs.org/measured-core/-/measured-core-1.51.1.tgz", @@ -23404,6 +23439,12 @@ "@types/range-parser": "*" } }, + "@types/geojson": { + "version": "7946.0.14", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz", + "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==", + "dev": true + }, "@types/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", @@ -28480,9 +28521,9 @@ "dev": true }, "lru-cache": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz", - "integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==" + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", + "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==" }, "make-dir": { "version": "2.1.0", @@ -28507,6 +28548,30 @@ "resolved": "https://registry.npmjs.org/mapcap/-/mapcap-1.0.0.tgz", "integrity": "sha512-KcNlZSlFPx+r1jYZmxEbTVymG+dIctf10WmWkuhrhrblM+KMoF77HelwihL5cxYlORye79KoR4IlOOk99lUJ0g==" }, + "mariadb": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/mariadb/-/mariadb-3.3.0.tgz", + "integrity": "sha512-sAL4bJgbfCAtXcE8bXI+NAMzVaPNkIU8hRZUXYfgNFoWB9U57G3XQiMeCx/A6IrS6y7kGwBLylrwgsZQ8kUYlw==", + "dev": true, + "requires": { + "@types/geojson": "^7946.0.14", + "@types/node": "^20.11.17", + "denque": "^2.1.0", + "iconv-lite": "^0.6.3", + "lru-cache": "^10.2.0" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, "measured-core": { "version": "1.51.1", "resolved": "https://registry.npmjs.org/measured-core/-/measured-core-1.51.1.tgz", diff --git a/package.json b/package.json index 66bc724c92d..66d57941f84 100644 --- a/package.json +++ b/package.json @@ -184,6 +184,7 @@ "koa-bodyparser": "^4.3.0", "koa-router": "^12.0.0", "lambda-local": "^2.0.2", + "mariadb": "^3.3.0", "memcached": "^2.2.2", "mimic-response": "1.0.0", "mkdirp": "^3.0.1", diff --git a/test/docker-compose.yml b/test/docker-compose.yml index a87e4435c31..a480fd5ce22 100644 --- a/test/docker-compose.yml +++ b/test/docker-compose.yml @@ -61,6 +61,20 @@ services: timeout: 10s retries: 30 + mariadb: + image: bitnami/mariadb:11.2 + environment: + ALLOW_EMPTY_PASSWORD: 1 + ports: + - "3306:3306" + volumes: + - nodemariadata:/bitnami/mariadb + healthcheck: + test: ["CMD", "mariadb" ,"-h", "mariadb", "-P", "3306", "-u", "root", "-e", "SELECT 1"] + interval: 1s + timeout: 10s + retries: 30 + redis: image: redis ports: @@ -185,6 +199,8 @@ volumes: driver: local nodemysqldata: driver: local + nodemariadata: + driver: local nodeesdata: driver: local nodecassandradata: diff --git a/test/instrumentation/modules/mariadb/_utils.js b/test/instrumentation/modules/mariadb/_utils.js new file mode 100644 index 00000000000..63264a176c2 --- /dev/null +++ b/test/instrumentation/modules/mariadb/_utils.js @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and other contributors where applicable. + * Licensed under the BSD 2-Clause License; you may not use this file except in + * compliance with the BSD 2-Clause License. + */ + +'use strict'; + +var mysql = require('mysql2'); + +exports.reset = reset; +exports.credentials = credentials; + +var DEFAULTS = { + host: process.env.MYSQL_HOST || 'localhost', + user: process.env.MYSQL_USER || 'root', + password: process.env.MYSQL_PASSWORD, + database: process.env.MYSQL_DATABASE || 'test_elastic_apm', +}; + +function credentials(conf) { + return Object.assign({}, DEFAULTS, conf); +} + +function reset(cb) { + var client = mysql.createConnection(credentials({ database: 'mysql' })); + + client.connect(function (err) { + if (err) throw err; + client.query('DROP DATABASE IF EXISTS test_elastic_apm', function (err) { + if (err) throw err; + client.query('CREATE DATABASE test_elastic_apm', function (err) { + if (err) throw err; + client.end(cb); + }); + }); + }); +} diff --git a/test/instrumentation/modules/mariadb/mariadb.test.js b/test/instrumentation/modules/mariadb/mariadb.test.js new file mode 100644 index 00000000000..99d0d00a705 --- /dev/null +++ b/test/instrumentation/modules/mariadb/mariadb.test.js @@ -0,0 +1,640 @@ +/* + * Copyright Elasticsearch B.V. and other contributors where applicable. + * Licensed under the BSD 2-Clause License; you may not use this file except in + * compliance with the BSD 2-Clause License. + */ + +'use strict'; + +if (process.env.GITHUB_ACTIONS === 'true' && process.platform === 'win32') { + console.log('# SKIP: GH Actions do not support docker services on Windows'); + process.exit(0); +} + +const semver = require('semver'); +const { safeGetPackageVersion } = require('../../../_utils'); +const mariadbVer = safeGetPackageVersion('mariadb'); +if (semver.gte(mariadbVer, '3.0.0') && semver.lt(process.version, '14.6.0')) { + console.log( + `# SKIP mysql2@${mariadbVer} does not support node ${process.version}`, + ); + process.exit(); +} + +var agent = require('../../../..').start({ + serviceName: 'test', + secretToken: 'test', + captureExceptions: false, + metricsInterval: 0, + centralConfig: false, + spanCompressionEnabled: false, +}); + +var mariadb = require('mariadb/callback'); +var mariadbPromise = require('mariadb'); +var test = require('tape'); + +var utils = require('./_utils'); +var mockClient = require('../../../_mock_http_client'); +var findObjInArray = require('../../../_utils').findObjInArray; + +var connectionOptions = utils.credentials(); +var queryable; +var queryablePromise; +var factories = [ + [createConnection, 'connection'], + [createPool, 'pool'], + [createPoolAndGetConnection, 'pool > connection'], + [createPoolClusterAndGetConnection, 'poolCluster > connection'], + // [createPoolClusterAndGetConnectionViaOf, 'poolCluster > of > connection'], +]; +// var executors = ['execute']; +// var executors = ['queryStream']; +var executors = ['query', 'execute', 'queryStream']; + +var universalArgumentSets = [ + { + names: ['sql'], + query: 'SELECT 1 + 1 AS solution', + values: (query, cb) => [query, cb], + }, + { + names: ['sql', 'values'], + query: 'SELECT 1 + ? AS solution', + values: (query, cb) => [query, ['1'], cb], + }, + { + names: ['options'], + query: 'SELECT 1 + 1 AS solution', + values: (query, cb) => [{ sql: query }, cb], + }, + { + names: ['options', 'values'], + query: 'SELECT 1 + ? AS solution', + values: (query, cb) => [{ sql: query }, ['1'], cb], + }, +]; + +factories.forEach(function (f) { + var factory = f[0]; + var type = f[1]; + // var hasPromises = f[2]; + + test('mariadb.' + factory.name, function (t) { + t.on('end', teardown); + executors.forEach(function (executor) { + t.test(executor, function (t) { + var isQuery = executor === 'query'; + var argumentSets = universalArgumentSets; + + if (executor === 'queryStream') { + if (type === 'pool') { + t.end(); + } else { + t.end(); + // t.test('streaming', function (t) { + // argumentSets.forEach(function (argumentSet) { + // var query = argumentSet.query; + // var names = argumentSet.names; + // var values = argumentSet.values; + // var name = `${type}.${executor}(${names.join(', ')})`; + // var args = values(query); + // t.test(name, function (t) { + // resetAgent(function (data) { + // assertBasicQuery(t, query, data); + // t.end(); + // }); + // factory(function () { + // agent.startTransaction('foo'); + // var stream = queryablePromise[executor].apply( + // queryablePromise, + // args, + // ); + // t.ok( + // agent.currentSpan === null, + // 'mariadb span should not spill into calling code', + // ); + // basicQueryStream(stream, t); + // }); + // }); + // }); + // }); + } + } else { + t.test('callback', function (t) { + argumentSets.forEach(function (argumentSet) { + var query = argumentSet.query; + var names = argumentSet.names; + var values = argumentSet.values; + + var name = `${type}.${executor}(${names.join(', ')}, callback)`; + var args = values(query, basicQueryCallback(t)); + + t.test(name, function (t) { + resetAgent(function (data) { + assertBasicQuery(t, query, data); + t.end(); + }); + factory(function () { + agent.startTransaction('foo'); + console.log('EMPEZANDO'); + queryable[executor].apply(queryable, args); + t.ok( + agent.currentSpan === null, + 'mariadb span should not spill into calling code', + ); + }); + }); + }); + }); + + // t.test('promise', function (t) { + // universalArgumentSets.forEach(function (argumentSet) { + // var query = argumentSet.query; + // var names = argumentSet.names; + // var values = argumentSet.values; + + // var name = `${type}.${executor}(${names.join(', ')})`; + // var args = values(query); + + // t.test(name, function (t) { + // resetAgent(function (data) { + // assertBasicQuery(t, query, data); + // t.end(); + // }); + // factory(function () { + // agent.startTransaction('foo'); + // var promise = queryablePromise[executor].apply( + // queryablePromise, + // args, + // ); + // t.ok( + // agent.currentSpan === null, + // 'mariadb span should not spill into calling code', + // ); + // basicQueryPromise(t, promise); + // }); + // }); + // }); + // }); + } + }); + }); + + // t.test('simultaneous queries', function (t) { + // t.test('on same connection', function (t) { + // resetAgent(4, function (data) { + // t.strictEqual(data.transactions.length, 1); + // t.strictEqual(data.spans.length, 3); + + // var trans = data.transactions[0]; + + // t.strictEqual(trans.name, 'foo'); + + // data.spans.forEach(function (span) { + // assertSpan(t, span, sql); + // t.equal( + // span.parent_id, + // trans.id, + // 'each mysql2 span is a child of the transaction', + // ); + // }); + + // t.end(); + // }); + + // var sql = 'SELECT 1 + ? AS solution'; + + // factory(function () { + // var n = 0; + // var trans = agent.startTransaction('foo'); + + // queryable.query(sql, [1], function (err, rows, fields) { + // t.error(err); + // t.strictEqual(rows[0].solution, 2); + // if (++n === 3) done(); + // }); + // queryable.query(sql, [2], function (err, rows, fields) { + // t.error(err); + // t.strictEqual(rows[0].solution, 3); + // if (++n === 3) done(); + // }); + // queryable.query(sql, [3], function (err, rows, fields) { + // t.error(err); + // t.strictEqual(rows[0].solution, 4); + // if (++n === 3) done(); + // }); + + // function done() { + // trans.end(); + // } + // }); + // }); + + // t.test('on different connections', function (t) { + // resetAgent(4, function (data) { + // t.strictEqual(data.transactions.length, 1); + // t.strictEqual(data.spans.length, 3); + + // var trans = data.transactions[0]; + + // t.strictEqual(trans.name, 'foo'); + + // data.spans.forEach(function (span) { + // assertSpan(t, span, sql); + // t.equal( + // span.parent_id, + // trans.id, + // 'each mysql2 span is a child of the transaction', + // ); + // }); + + // t.end(); + // }); + + // var sql = 'SELECT 1 + ? AS solution'; + + // createPool(function () { + // var n = 0; + // var trans = agent.startTransaction('foo'); + + // queryable.getConnection(function (err, conn) { + // t.error(err); + // conn.query(sql, [1], function (err, rows, fields) { + // t.error(err); + // t.strictEqual(rows[0].solution, 2); + // if (++n === 3) done(); + // }); + // }); + // queryable.getConnection(function (err, conn) { + // t.error(err); + // conn.query(sql, [2], function (err, rows, fields) { + // t.error(err); + // t.strictEqual(rows[0].solution, 3); + // if (++n === 3) done(); + // }); + // }); + // queryable.getConnection(function (err, conn) { + // t.error(err); + // conn.query(sql, [3], function (err, rows, fields) { + // t.error(err); + // t.strictEqual(rows[0].solution, 4); + // if (++n === 3) done(); + // }); + // }); + + // function done() { + // trans.end(); + // } + // }); + // }); + // }); + + // t.test('simultaneous transactions', function (t) { + // resetAgent(6, function (data) { + // t.strictEqual(data.transactions.length, 3); + // t.strictEqual(data.spans.length, 3); + // var names = data.transactions + // .map(function (trans) { + // return trans.name; + // }) + // .sort(); + // t.deepEqual(names, ['bar', 'baz', 'foo']); + + // data.transactions.forEach(function (trans) { + // const span = findObjInArray(data.spans, 'transaction_id', trans.id); + // t.ok(span, 'transaction should have span'); + // assertSpan(t, span, sql); + // }); + + // t.end(); + // }); + + // var sql = 'SELECT 1 + ? AS solution'; + + // factory(function () { + // setImmediate(function () { + // var trans = agent.startTransaction('foo'); + // queryable.query(sql, [1], function (err, rows, fields) { + // t.error(err); + // t.strictEqual(rows[0].solution, 2); + // trans.end(); + // }); + // }); + + // setImmediate(function () { + // var trans = agent.startTransaction('bar'); + // queryable.query(sql, [2], function (err, rows, fields) { + // t.error(err); + // t.strictEqual(rows[0].solution, 3); + // trans.end(); + // }); + // }); + + // setImmediate(function () { + // var trans = agent.startTransaction('baz'); + // queryable.query(sql, [3], function (err, rows, fields) { + // t.error(err); + // t.strictEqual(rows[0].solution, 4); + // trans.end(); + // }); + // }); + // }); + // }); + + // // Only pools have a getConnection function + // if (type === 'pool') { + // t.test('connection.release()', function (t) { + // resetAgent(function (data) { + // assertBasicQuery(t, sql, data); + // t.end(); + // }); + + // var sql = 'SELECT 1 + 1 AS solution'; + + // factory(function () { + // agent.startTransaction('foo'); + + // queryable.getConnection(function (err, conn) { + // t.error(err); + // conn.release(); + + // queryable.getConnection(function (err, conn) { + // t.error(err); + // conn.query(sql, basicQueryCallback(t)); + // t.ok( + // agent.currentSpan === null, + // 'mysql2 span should not spill into calling code', + // ); + // }); + // }); + // }); + // }); + // } + }); +}); + +function basicQueryPromise(t, p) { + function done() { + agent.endTransaction(); + } + + p.then( + function (response) { + console.log('Ejecutada'); + var rows = response[0]; + t.strictEqual(rows.solution, 2); + done(); + }, + function (error) { + t.error(error); + done(); + }, + ); +} + +function basicQueryCallback(t) { + return function (err, rows, fields) { + console.log('CALLBACK'); + t.ok( + agent.currentSpan === null, + 'mariadb span should not spill into calling code', + ); + t.error(err); + t.strictEqual(rows[0].solution, 2); + agent.endTransaction(); + }; +} + +function basicQueryStream(stream, t) { + var results = 0; + stream.on('error', function (err) { + t.ok( + agent.currentSpan === null, + 'mariadb span should not be active in user code', + ); + t.error(err); + }); + stream.on('result', function (row) { + t.ok( + agent.currentSpan === null, + 'mysql2 span should not be active in user code', + ); + results++; + t.strictEqual(row.solution, 2); + }); + stream.on('end', function () { + t.ok( + agent.currentSpan === null, + 'mysql2 span should not be active in user code', + ); + t.strictEqual(results, 1); + agent.endTransaction(); + }); +} + +function assertBasicQuery(t, sql, data) { + t.strictEqual(data.transactions.length, 1); + t.strictEqual(data.spans.length, 1); + + var trans = data.transactions[0]; + var span = data.spans[0]; + + t.strictEqual(trans.name, 'foo'); + assertSpan(t, span, sql); +} + +function assertSpan(t, span, sql) { + t.strictEqual(span.name, 'SELECT', 'span.name'); + t.strictEqual(span.type, 'db', 'span.type'); + t.strictEqual(span.subtype, 'mariadb', 'span.subtype'); + t.strictEqual(span.action, 'query', 'span.action'); + t.deepEqual( + span.context.db, + { + type: 'sql', + instance: connectionOptions.database, + user: connectionOptions.user, + statement: sql, + }, + 'span.context.db', + ); + t.deepEqual( + span.context.service.target, + { + type: 'mariadb', + name: connectionOptions.database, + }, + 'span.context.service.target', + ); + t.deepEqual( + span.context.destination, + { + address: connectionOptions.host, + port: 3306, + service: { + type: '', + name: '', + resource: `mariadb/${connectionOptions.database}`, + }, + }, + 'span.context.destination', + ); +} + +function createConnection(cb) { + setup(function () { + _teardown = function teardown() { + console.log('CERRANDO'); + if (queryable) { + queryable.end(); + queryable = undefined; + } + if (queryablePromise) { + queryablePromise.end(); + queryablePromise = undefined; + } + }; + + queryable = mariadb.createConnection(connectionOptions); + + queryable.connect((err) => { + if (err) throw err; + + mariadbPromise.createConnection(connectionOptions).then(function (conn2) { + queryablePromise = conn2; + cb(); + }); + }); + }); +} + +function createPool(cb) { + setup(function () { + _teardown = function teardown() { + if (queryable) { + queryable.end(); + queryable = undefined; + } + if (queryablePromise) { + queryablePromise.end(); + queryablePromise = undefined; + } + }; + + queryable = mariadb.createPool(connectionOptions); + queryablePromise = mariadbPromise.createPool(connectionOptions); + + cb(); + }); +} + +function createPoolAndGetConnection(cb) { + setup(function () { + _teardown = function teardown() { + if (pool) { + queryable.end(); + pool.end(); + pool = undefined; + queryable = undefined; + } + if (poolPromise) { + queryablePromise.end(); + poolPromise.end(); + poolPromise = undefined; + queryablePromise = undefined; + } + }; + + var pool = mariadb.createPool(connectionOptions); + var poolPromise = mariadbPromise.createPool(connectionOptions); + + pool.getConnection(function (err, conn) { + if (err) throw err; + queryable = conn; + + poolPromise.getConnection().then(function (conn) { + queryablePromise = conn; + cb(); + }); + }); + }); +} + +function createPoolClusterAndGetConnection(cb) { + setup(function () { + _teardown = function teardown() { + if (cluster) { + queryable.end(); + cluster.end(); + cluster = undefined; + queryable = undefined; + } + if (clusterPromise) { + queryablePromise.end(); + clusterPromise.end(); + clusterPromise = undefined; + queryablePromise = undefined; + } + }; + + var cluster = mariadb.createPoolCluster(); + var clusterPromise = mariadbPromise.createPoolCluster(); + + cluster.add('master', connectionOptions); + clusterPromise.add('master', connectionOptions); + + cluster.getConnection(function (err, conn) { + if (err) throw err; + queryable = conn; + + clusterPromise.getConnection().then(function (conn2) { + queryablePromise = conn2; + cb(); + }); + }); + }); +} + +function createPoolClusterAndGetConnectionViaOf(cb) { + setup(function () { + _teardown = function teardown() { + if (cluster) { + queryable.end(); + cluster.end(); + cluster = undefined; + queryable = undefined; + } + }; + + var cluster = mariadb.createPoolCluster(); + cluster.add('master-test', connectionOptions); + cluster.of('.*', 'RANDOM').getConnection(function (err, conn) { + console.log('OBTENIDA'); + if (err) throw err; + queryable = conn; + cb(); + }); + }); +} + +function setup(cb) { + teardown(); // just in case it didn't happen at the end of the previous test + utils.reset(cb); +} + +// placeholder variable to hold the teardown function created by the setup function +var _teardown = function () { }; +var teardown = function () { + _teardown(); +}; + +function resetAgent(expected, cb) { + if (typeof expected === 'function') return resetAgent(2, expected); + // first time this function is called, the real client will be present - so + // let's just destroy it before creating the mock + + if (agent._apmClient.destroy) agent._apmClient.destroy(); + agent._instrumentation.testReset(); + agent._apmClient = mockClient(expected, cb); +} diff --git a/test/instrumentation/modules/mariadb/minitest-cb.test.js b/test/instrumentation/modules/mariadb/minitest-cb.test.js new file mode 100644 index 00000000000..39149e4ced6 --- /dev/null +++ b/test/instrumentation/modules/mariadb/minitest-cb.test.js @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and other contributors where applicable. + * Licensed under the BSD 2-Clause License; you may not use this file except in + * compliance with the BSD 2-Clause License. + */ + +var agent = require('../../../..').start({ + secretToken: 'test', + serviceName: 'test-mariadb-integration', + serverUrl: 'https://apm-server-lo6wr9-pre.mm-red.net', + environment: 'test', + captureExceptions: false, + metricsInterval: 0, + centralConfig: false, + spanCompressionEnabled: false, +}); + +const mariadb = require('mariadb/callback'); + +function asyncFunction() { + let pool; + try { + pool = mariadb.createPool({ + host: 'mariadb.test.orb.local', + user: 'root', + connectionLimit: 5, + }); + + pool.getConnection(function (err, conn) { + if (err) throw err; + conn.query('SELECT 1 + ? as val', [1], function (err, rows, fields) { + conn.release(); + if (err) throw err; + // rows: [ {val: 1}, meta: ... ] + console.log(rows); + // const res = await conn.query('INSERT INTO myTable value (?, ?)', [ + // 1, + // 'mariadb', + // ]); + // res: { affectedRows: 1, insertId: 1, warningStatus: 0 } + agent.endTransaction('foo'); + pool.end(); + }); + }); + + // agent.startTransaction('foo'); + // pool.query('SELECT 1 + ? as val', [1], function (err, rows, fields) { + // pool.end(); + // if (err) throw err; + // // rows: [ {val: 1}, meta: ... ] + // console.log(rows); + // // const res = await conn.query('INSERT INTO myTable value (?, ?)', [ + // // 1, + // // 'mariadb', + // // ]); + // // res: { affectedRows: 1, insertId: 1, warningStatus: 0 } + // agent.endTransaction('foo'); + // }); + } finally { + // if (pool) pool.end(); //release to pool + } +} + +asyncFunction(); diff --git a/test/instrumentation/modules/mariadb/minitest-cluster.test.js b/test/instrumentation/modules/mariadb/minitest-cluster.test.js new file mode 100644 index 00000000000..4963306b51d --- /dev/null +++ b/test/instrumentation/modules/mariadb/minitest-cluster.test.js @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and other contributors where applicable. + * Licensed under the BSD 2-Clause License; you may not use this file except in + * compliance with the BSD 2-Clause License. + */ + +(async function () { + var agent = require('../../../..').start({ + secretToken: 'test', + serviceName: 'test-mariadb-integration', + serverUrl: 'https://apm-server-lo6wr9-pre.mm-red.net', + environment: 'test', + captureExceptions: false, + metricsInterval: 0, + centralConfig: false, + spanCompressionEnabled: false, + }); + + const mariadb = require('mariadb'); + + const pool = mariadb.createPoolCluster(); + + async function asyncFunction() { + let conn; + try { + pool.add('master', { + host: 'mariadb.test.orb.local', + user: 'root', + connectionLimit: 5, + }); + agent.startTransaction('foo'); + conn = await pool.getConnection(); + + const rows = await conn.query('SELECT 1 + ? as val', [1]); + // rows: [ {val: 1}, meta: ... ] + console.log(rows); + // const res = await conn.query('INSERT INTO myTable value (?, ?)', [ + // 1, + // 'mariadb', + // ]); + // res: { affectedRows: 1, insertId: 1, warningStatus: 0 } + agent.endTransaction('foo'); + } finally { + if (conn) conn.end(); //release to pool + } + } + + await asyncFunction(); + await pool.end(); +})(); diff --git a/test/instrumentation/modules/mariadb/minitest.test.js b/test/instrumentation/modules/mariadb/minitest.test.js new file mode 100644 index 00000000000..99de60649e6 --- /dev/null +++ b/test/instrumentation/modules/mariadb/minitest.test.js @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and other contributors where applicable. + * Licensed under the BSD 2-Clause License; you may not use this file except in + * compliance with the BSD 2-Clause License. + */ + +(async function () { + var agent = require('../../../..').start({ + secretToken: 'test', + serviceName: 'test-mariadb-integration', + serverUrl: 'https://apm-server-lo6wr9-pre.mm-red.net', + environment: 'test', + captureExceptions: false, + metricsInterval: 0, + centralConfig: false, + spanCompressionEnabled: false, + }); + + const mariadb = require('mariadb'); + + const pool = mariadb.createPool({ + host: 'mariadb.test.orb.local', + user: 'root', + connectionLimit: 5, + }); + + async function asyncFunction() { + let conn; + try { + agent.startTransaction('foo'); + conn = await mariadb.createConnection({ + host: 'mariadb.test.orb.local', + user: 'root', + connectionLimit: 5, + }); + + const rows = await conn.execute('SELECT 1 + ? as val', [1]); + // rows: [ {val: 1}, meta: ... ] + console.log(rows); + // const res = await conn.query('INSERT INTO myTable value (?, ?)', [ + // 1, + // 'mariadb', + // ]); + // res: { affectedRows: 1, insertId: 1, warningStatus: 0 } + agent.endTransaction('foo'); + } finally { + if (conn) conn.end(); //release to pool + } + } + + await asyncFunction(); + await pool.end(); +})(); diff --git a/test/instrumentation/modules/mariadb/pool-release-1.test.js b/test/instrumentation/modules/mariadb/pool-release-1.test.js new file mode 100644 index 00000000000..7475e995fa9 --- /dev/null +++ b/test/instrumentation/modules/mariadb/pool-release-1.test.js @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and other contributors where applicable. + * Licensed under the BSD 2-Clause License; you may not use this file except in + * compliance with the BSD 2-Clause License. + */ + +'use strict'; + +if (process.env.GITHUB_ACTIONS === 'true' && process.platform === 'win32') { + console.log('# SKIP: GH Actions do not support docker services on Windows'); + process.exit(0); +} + +const semver = require('semver'); +const { safeGetPackageVersion } = require('../../../_utils'); +const mysql2Ver = safeGetPackageVersion('mysql2'); +if (semver.gte(mysql2Ver, '3.0.0') && semver.lt(process.version, '14.6.0')) { + console.log( + `# SKIP mysql2@${mysql2Ver} does not support node ${process.version}`, + ); + process.exit(); +} + +var agent = require('../../../..').start({ + serviceName: 'test', + secretToken: 'test', + captureExceptions: false, + metricsInterval: 0, + centralConfig: false, +}); + +var mysql = require('mysql2'); +var test = require('tape'); + +var utils = require('./_utils'); + +test('release connection prior to transaction', function (t) { + createPool(function (pool) { + pool.getConnection(function (err, conn) { + t.error(err); + conn.release(); // important to release connection before starting the transaction + + agent.startTransaction('foo'); + t.ok(agent.currentTransaction); + + pool.getConnection(function (err, conn) { + t.error(err); + t.ok(agent.currentTransaction); + pool.end(); + t.end(); + }); + }); + }); +}); + +function createPool(cb) { + setup(function () { + cb(mysql.createPool(utils.credentials())); + }); +} + +function setup(cb) { + utils.reset(cb); +} From 3de549c4bad4b699c190561c1ab64b833462ec9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eneko=20Rodr=C3=ADguez?= Date: Fri, 24 May 2024 21:01:10 +0200 Subject: [PATCH 02/13] passing all ways --- lib/instrumentation/modules/mariadb.js | 51 ++++- .../modules/mariadb/mariadb.test.js | 199 ++++++++++-------- 2 files changed, 153 insertions(+), 97 deletions(-) diff --git a/lib/instrumentation/modules/mariadb.js b/lib/instrumentation/modules/mariadb.js index d36c3f722fc..53d4ee3b6c1 100644 --- a/lib/instrumentation/modules/mariadb.js +++ b/lib/instrumentation/modules/mariadb.js @@ -42,6 +42,7 @@ module.exports = function (mariadb, agent, { version, enabled, name }) { return function wrappedPool() { console.log('Inserting wrapPool'); let result = original.apply(this, arguments); + console.log('Result', result); shimmer.wrap( result, 'getConnection', @@ -58,9 +59,12 @@ module.exports = function (mariadb, agent, { version, enabled, name }) { return wrapConnection.apply(result, args); }, ); + console.log('Result2', result); if (typeof result.add === 'function') { shimmer.wrap(result, 'add', wrapAdd); + } + if (typeof result.of === 'function') { shimmer.wrap(result, 'of', wrapPool); } @@ -104,26 +108,43 @@ module.exports = function (mariadb, agent, { version, enabled, name }) { if (!name.includes('callback')) { console.log('Shimming promise'); return original.apply(this, arguments).then((res) => { - shimmer.wrap(res, 'query', wrapQuery); - shimmer.wrap(res, 'execute', wrapQuery); - shimmer.wrap(res, 'queryStream', wrapQuery); + if (typeof res.query === 'function') { + shimmer.wrap(res, 'query', wrapQuery); + } + if (typeof res.execute === 'function') { + shimmer.wrap(res, 'execute', wrapQuery); + } + + if (typeof res.queryStream === 'function') { + shimmer.wrap(res, 'queryStream', wrapQuery); + } return Promise.resolve(res); }); } console.log('test'); - if (typeof arguments[0] === 'function') { - console.log('Shimming callback'); + if (typeof arguments[arguments.length - 1] === 'function') { + console.log('Shimming callback', original); + return original.apply(this, [ + ...[...arguments].slice(0, arguments.length - 1), (err, conn) => { console.log('MI CALLBACK'); console.log(err); if (err) return arguments[0](err); console.log('test345'); - shimmer.wrap(conn, 'query', wrapQuery); - shimmer.wrap(conn, 'execute', wrapQuery); - // shimmer.wrap(conn, 'queryStream', wrapQuery); + + if (typeof conn.query === 'function') { + shimmer.wrap(conn, 'query', wrapQuery); + } + if (typeof conn.execute === 'function') { + shimmer.wrap(conn, 'execute', wrapQuery); + } + + if (typeof conn.queryStream === 'function') { + shimmer.wrap(conn, 'queryStream', wrapQuery); + } return arguments[0](err, conn); }, @@ -132,9 +153,17 @@ module.exports = function (mariadb, agent, { version, enabled, name }) { console.log('Shimming WITHOUT callback'); let result = original.apply(this, arguments); - shimmer.wrap(result, 'query', wrapQuery); - shimmer.wrap(result, 'execute', wrapQuery); - // shimmer.wrap(conn, 'queryStream', wrapQuery); + + if (typeof result.query === 'function') { + shimmer.wrap(result, 'query', wrapQuery); + } + if (typeof result.execute === 'function') { + shimmer.wrap(result, 'execute', wrapQuery); + } + + if (typeof result.queryStream === 'function') { + shimmer.wrap(result, 'queryStream', wrapQuery); + } return result; } diff --git a/test/instrumentation/modules/mariadb/mariadb.test.js b/test/instrumentation/modules/mariadb/mariadb.test.js index 99d0d00a705..5a01b9f1e8d 100644 --- a/test/instrumentation/modules/mariadb/mariadb.test.js +++ b/test/instrumentation/modules/mariadb/mariadb.test.js @@ -42,15 +42,28 @@ var connectionOptions = utils.credentials(); var queryable; var queryablePromise; var factories = [ - [createConnection, 'connection'], - [createPool, 'pool'], - [createPoolAndGetConnection, 'pool > connection'], - [createPoolClusterAndGetConnection, 'poolCluster > connection'], - // [createPoolClusterAndGetConnectionViaOf, 'poolCluster > of > connection'], + [createConnection, 'connection', true], + [createPool, 'pool', true], + [createPoolAndGetConnection, 'pool > connection', true], + [createPoolClusterAndGetConnection, 'poolCluster > connection', true], + [ + createPoolClusterAndGetConnectionViaOf, + 'poolCluster > of > connection', + false, + ], + [ + createPoolClusterAndGetConnectionViaOfDirect, + 'poolCluster > ofConnection', + false, + ], ]; // var executors = ['execute']; // var executors = ['queryStream']; -var executors = ['query', 'execute', 'queryStream']; +var executors = [ + 'query', + 'execute', + // 'queryStream', +]; var universalArgumentSets = [ { @@ -78,7 +91,7 @@ var universalArgumentSets = [ factories.forEach(function (f) { var factory = f[0]; var type = f[1]; - // var hasPromises = f[2]; + var hasCallback = f[2]; test('mariadb.' + factory.name, function (t) { t.on('end', teardown); @@ -88,47 +101,14 @@ factories.forEach(function (f) { var argumentSets = universalArgumentSets; if (executor === 'queryStream') { - if (type === 'pool') { - t.end(); - } else { - t.end(); - // t.test('streaming', function (t) { - // argumentSets.forEach(function (argumentSet) { - // var query = argumentSet.query; - // var names = argumentSet.names; - // var values = argumentSet.values; - // var name = `${type}.${executor}(${names.join(', ')})`; - // var args = values(query); - // t.test(name, function (t) { - // resetAgent(function (data) { - // assertBasicQuery(t, query, data); - // t.end(); - // }); - // factory(function () { - // agent.startTransaction('foo'); - // var stream = queryablePromise[executor].apply( - // queryablePromise, - // args, - // ); - // t.ok( - // agent.currentSpan === null, - // 'mariadb span should not spill into calling code', - // ); - // basicQueryStream(stream, t); - // }); - // }); - // }); - // }); - } - } else { - t.test('callback', function (t) { + t.test('streaming', function (t) { argumentSets.forEach(function (argumentSet) { var query = argumentSet.query; var names = argumentSet.names; var values = argumentSet.values; - var name = `${type}.${executor}(${names.join(', ')}, callback)`; - var args = values(query, basicQueryCallback(t)); + var name = `${type}.${executor}(${names.join(', ')})`; + var args = values(query); t.test(name, function (t) { resetAgent(function (data) { @@ -137,46 +117,73 @@ factories.forEach(function (f) { }); factory(function () { agent.startTransaction('foo'); - console.log('EMPEZANDO'); - queryable[executor].apply(queryable, args); + var stream = queryablePromise[executor].apply( + queryable, + args, + ); + t.ok( + agent.currentSpan === null, + 'mysql2 span should not spill into calling code', + ); + basicQueryStream(stream, t); + }); + }); + }); + }); + } else { + if (hasCallback) { + t.test('callback', function (t) { + argumentSets.forEach(function (argumentSet) { + var query = argumentSet.query; + var names = argumentSet.names; + var values = argumentSet.values; + var name = `${type}.${executor}(${names.join(', ')}, callback)`; + var args = values(query, basicQueryCallback(t)); + t.test(name, function (t) { + resetAgent(function (data) { + assertBasicQuery(t, query, data); + t.end(); + }); + factory(function () { + agent.startTransaction('foo'); + console.log('EMPEZANDO'); + queryable[executor].apply(queryable, args); + t.ok( + agent.currentSpan === null, + 'mariadb span should not spill into calling code', + ); + }); + }); + }); + }); + } + t.test('promise', function (t) { + universalArgumentSets.forEach(function (argumentSet) { + var query = argumentSet.query; + var names = argumentSet.names; + var values = argumentSet.values; + var name = `${type}.${executor}(${names.join(', ')})`; + var args = values(query); + t.test(name, function (t) { + resetAgent(function (data) { + assertBasicQuery(t, query, data); + t.end(); + }); + factory(function () { + agent.startTransaction('foo'); + var promise = queryablePromise[executor].apply( + queryablePromise, + args, + ); t.ok( agent.currentSpan === null, 'mariadb span should not spill into calling code', ); + basicQueryPromise(t, promise); }); }); }); }); - - // t.test('promise', function (t) { - // universalArgumentSets.forEach(function (argumentSet) { - // var query = argumentSet.query; - // var names = argumentSet.names; - // var values = argumentSet.values; - - // var name = `${type}.${executor}(${names.join(', ')})`; - // var args = values(query); - - // t.test(name, function (t) { - // resetAgent(function (data) { - // assertBasicQuery(t, query, data); - // t.end(); - // }); - // factory(function () { - // agent.startTransaction('foo'); - // var promise = queryablePromise[executor].apply( - // queryablePromise, - // args, - // ); - // t.ok( - // agent.currentSpan === null, - // 'mariadb span should not spill into calling code', - // ); - // basicQueryPromise(t, promise); - // }); - // }); - // }); - // }); } }); }); @@ -599,20 +606,40 @@ function createPoolClusterAndGetConnection(cb) { function createPoolClusterAndGetConnectionViaOf(cb) { setup(function () { _teardown = function teardown() { - if (cluster) { - queryable.end(); - cluster.end(); - cluster = undefined; - queryable = undefined; + if (clusterPromise) { + queryablePromise.end(); + clusterPromise.end(); + clusterPromise = undefined; + queryablePromise = undefined; } }; - var cluster = mariadb.createPoolCluster(); - cluster.add('master-test', connectionOptions); - cluster.of('.*', 'RANDOM').getConnection(function (err, conn) { - console.log('OBTENIDA'); - if (err) throw err; - queryable = conn; + var clusterPromise = mariadbPromise.createPoolCluster(); + clusterPromise.add('master-test', connectionOptions); + clusterPromise + .of('.*', 'RANDOM') + .getConnection() + .then((conn) => { + queryablePromise = conn; + cb(); + }); + }); +} +function createPoolClusterAndGetConnectionViaOfDirect(cb) { + setup(function () { + _teardown = function teardown() { + if (clusterPromise) { + queryablePromise.end(); + clusterPromise.end(); + clusterPromise = undefined; + queryablePromise = undefined; + } + }; + + var clusterPromise = mariadbPromise.createPoolCluster(); + clusterPromise.add('master-test', connectionOptions); + clusterPromise.getConnection('.*', 'RANDOM').then((conn) => { + queryablePromise = conn; cb(); }); }); From bfd090c0a4a83e4abd1caa989428fe1975efaf55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eneko=20Rodr=C3=ADguez?= Date: Fri, 24 May 2024 21:58:22 +0200 Subject: [PATCH 03/13] progress --- lib/instrumentation/modules/mariadb.js | 53 ++++----- .../modules/mariadb/mariadb.test.js | 104 ++++++++++++------ .../modules/mariadb/minitest.test.js | 8 +- 3 files changed, 100 insertions(+), 65 deletions(-) diff --git a/lib/instrumentation/modules/mariadb.js b/lib/instrumentation/modules/mariadb.js index 53d4ee3b6c1..36bf3a8ec18 100644 --- a/lib/instrumentation/modules/mariadb.js +++ b/lib/instrumentation/modules/mariadb.js @@ -12,7 +12,6 @@ var sqlSummary = require('sql-summary'); var shimmer = require('../shimmer'); var symbols = require('../../symbols'); var { getDBDestination } = require('../context'); -const { config } = require('bluebird'); module.exports = function (mariadb, agent, { version, enabled, name }) { console.log('Starting mariadb instrumentation'); @@ -169,10 +168,10 @@ module.exports = function (mariadb, agent, { version, enabled, name }) { } }; } - function wrapQuery(original) { + function wrapQuery(original, name) { console.log('Inserting wrapQuery', original); return function wrappedQuery(sql, values, cb) { - console.log('Executing wrapQuery', arguments, config); + console.log('Executing wrapQuery', arguments); agent.logger.debug('intercepted call to mariadb.%s', original.name); var span = ins.createSpan(null, 'db', 'mariadb', 'query', { @@ -228,32 +227,7 @@ module.exports = function (mariadb, agent, { version, enabled, name }) { arguments[2] = wrapCallback(cb); } - const spanRunContext = ins.currRunContext().enterSpan(span); - const result = ins.withRunContext( - spanRunContext, - original, - this, - ...arguments, - ); - - if (result && !hasCallback) { - ins.bindEmitter(result); - console.log('EMITER'); - shimmer.wrap(result, 'emit', function (origEmit) { - return function (event) { - console.log('Evento de callback', event); - switch (event) { - case 'error': - case 'close': - case 'send_end': - span.end(); - } - return origEmit.apply(this, arguments); - }; - }); - } - - if (!hasCallback) { + if (!hasCallback && name !== 'queryStream') { console.log('Ejecuting promise'); return new Promise(async (resolve, reject) => { console.log('test1'); @@ -265,7 +239,26 @@ module.exports = function (mariadb, agent, { version, enabled, name }) { } console.log('Devolviendo normal sin '); console.log(arguments); - return original.apply(this, arguments); + + if (name === 'queryStream' || (name === 'query' && !hasCallback)) { + let newResult = original.apply(this, arguments); + + ins.bindEmitter(newResult); + shimmer.wrap(newResult, 'emit', function (origEmit) { + return function (event) { + console.log('Evento de callback', event, arguments); + switch (event) { + case 'error': + case 'end': + span.end(); + } + return origEmit.apply(this, arguments); + }; + }); + return newResult; + } else { + return original.apply(this, arguments); + } }; } }; diff --git a/test/instrumentation/modules/mariadb/mariadb.test.js b/test/instrumentation/modules/mariadb/mariadb.test.js index 5a01b9f1e8d..db41404cc22 100644 --- a/test/instrumentation/modules/mariadb/mariadb.test.js +++ b/test/instrumentation/modules/mariadb/mariadb.test.js @@ -62,7 +62,7 @@ var factories = [ var executors = [ 'query', 'execute', - // 'queryStream', + // 'queryStream' ]; var universalArgumentSets = [ @@ -101,35 +101,66 @@ factories.forEach(function (f) { var argumentSets = universalArgumentSets; if (executor === 'queryStream') { - t.test('streaming', function (t) { - argumentSets.forEach(function (argumentSet) { - var query = argumentSet.query; - var names = argumentSet.names; - var values = argumentSet.values; - - var name = `${type}.${executor}(${names.join(', ')})`; - var args = values(query); - - t.test(name, function (t) { - resetAgent(function (data) { - assertBasicQuery(t, query, data); - t.end(); - }); - factory(function () { - agent.startTransaction('foo'); - var stream = queryablePromise[executor].apply( - queryable, - args, - ); - t.ok( - agent.currentSpan === null, - 'mysql2 span should not spill into calling code', - ); - basicQueryStream(stream, t); - }); - }); - }); - }); + // if (!['pool'].includes(type)) { + // t.test('streaming promise', function (t) { + // argumentSets.forEach(function (argumentSet) { + // var query = argumentSet.query; + // var names = argumentSet.names; + // var values = argumentSet.values; + // var name = `${type}.${executor}(${names.join(', ')})`; + // var args = values(query); + // t.test(name, function (t) { + // resetAgent(function (data) { + // console.log('RESET'); + // assertBasicQuery(t, query, data); + // t.end(); + // }); + // factory(function () { + // console.log('INIT TRANSACIONC'); + // agent.startTransaction('foo'); + // var stream = queryablePromise[executor].apply( + // queryablePromise, + // args, + // ); + // t.ok( + // agent.currentSpan === null, + // 'mariadb span should not spill into calling code', + // ); + // basicQueryStream(stream, t); + // }); + // }); + // }); + // }); + // executor = 'query'; + // t.test('streaming callback', function (t) { + // argumentSets.forEach(function (argumentSet) { + // var query = argumentSet.query; + // var names = argumentSet.names; + // var values = argumentSet.values; + // var name = `${type}.${executor}(${names.join(', ')})`; + // var args = values(query); + // t.test(name, function (t) { + // resetAgent(function (data) { + // console.log('RESET'); + // assertBasicQuery(t, query, data); + // t.end(); + // }); + // factory(function () { + // console.log('INIT TRANSACIONC'); + // agent.startTransaction('foo'); + // var stream = queryable[executor].apply(queryable, args); + // t.ok( + // agent.currentSpan === null, + // 'mariadb span should not spill into calling code', + // ); + // basicQueryStream(stream, t); + // }); + // }); + // }); + // }); + // } else { + // t.end(); + // } } else { if (hasCallback) { t.test('callback', function (t) { @@ -416,26 +447,33 @@ function basicQueryCallback(t) { function basicQueryStream(stream, t) { var results = 0; stream.on('error', function (err) { + console.log('ERROR', err); t.ok( agent.currentSpan === null, 'mariadb span should not be active in user code', ); t.error(err); }); - stream.on('result', function (row) { + stream.on('data', function (row) { + console.log(agent.currentSpan); + + console.log('DATA'); t.ok( agent.currentSpan === null, - 'mysql2 span should not be active in user code', + 'mariadb span should not be active in user code', ); results++; t.strictEqual(row.solution, 2); }); stream.on('end', function () { + console.log('END'); + t.ok( agent.currentSpan === null, - 'mysql2 span should not be active in user code', + 'mariadb span should not be active in user code', ); t.strictEqual(results, 1); + console.log('testes', results); agent.endTransaction(); }); } diff --git a/test/instrumentation/modules/mariadb/minitest.test.js b/test/instrumentation/modules/mariadb/minitest.test.js index 99de60649e6..0e1831f79e9 100644 --- a/test/instrumentation/modules/mariadb/minitest.test.js +++ b/test/instrumentation/modules/mariadb/minitest.test.js @@ -34,9 +34,13 @@ connectionLimit: 5, }); - const rows = await conn.execute('SELECT 1 + ? as val', [1]); + const rows = await conn.queryStream('SELECT 1 + ? as val', [1]); + + for await (const row of rows) { + console.log(row); + } // rows: [ {val: 1}, meta: ... ] - console.log(rows); + // console.log(rows); // const res = await conn.query('INSERT INTO myTable value (?, ?)', [ // 1, // 'mariadb', From 8c608fc2df49578d636e7a2e5bc5fdec97d6eecc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eneko=20Rodr=C3=ADguez?= Date: Fri, 24 May 2024 22:07:12 +0200 Subject: [PATCH 04/13] streams working --- lib/instrumentation/modules/mariadb.js | 29 +++- .../modules/mariadb/mariadb.test.js | 128 +++++++++--------- 2 files changed, 87 insertions(+), 70 deletions(-) diff --git a/lib/instrumentation/modules/mariadb.js b/lib/instrumentation/modules/mariadb.js index 36bf3a8ec18..df2be4b8e33 100644 --- a/lib/instrumentation/modules/mariadb.js +++ b/lib/instrumentation/modules/mariadb.js @@ -13,7 +13,11 @@ var shimmer = require('../shimmer'); var symbols = require('../../symbols'); var { getDBDestination } = require('../context'); -module.exports = function (mariadb, agent, { version, enabled, name }) { +module.exports = function ( + mariadb, + agent, + { version, enabled, name: pkgName }, +) { console.log('Starting mariadb instrumentation'); if (!enabled) { return mariadb; @@ -60,6 +64,14 @@ module.exports = function (mariadb, agent, { version, enabled, name }) { ); console.log('Result2', result); + if (typeof arguments[0] === 'object') { + console.log('Cambiadno'); + config = { + ...defaultConfig, + ...arguments[0], + }; + } + if (typeof result.add === 'function') { shimmer.wrap(result, 'add', wrapAdd); } @@ -94,7 +106,7 @@ module.exports = function (mariadb, agent, { version, enabled, name }) { function wrapConnection(original) { return function wrappedConnection() { - console.log('Executing wrapConnection', name, arguments); + console.log('Executing wrapConnection', pkgName, arguments); if (typeof arguments[0] === 'object') { config = { @@ -104,7 +116,7 @@ module.exports = function (mariadb, agent, { version, enabled, name }) { } console.log(arguments); - if (!name.includes('callback')) { + if (!pkgName.includes('callback')) { console.log('Shimming promise'); return original.apply(this, arguments).then((res) => { if (typeof res.query === 'function') { @@ -227,7 +239,11 @@ module.exports = function (mariadb, agent, { version, enabled, name }) { arguments[2] = wrapCallback(cb); } - if (!hasCallback && name !== 'queryStream') { + if ( + !hasCallback && + name !== 'queryStream' && + (name !== 'query' || !pkgName.includes('callback')) + ) { console.log('Ejecuting promise'); return new Promise(async (resolve, reject) => { console.log('test1'); @@ -240,7 +256,10 @@ module.exports = function (mariadb, agent, { version, enabled, name }) { console.log('Devolviendo normal sin '); console.log(arguments); - if (name === 'queryStream' || (name === 'query' && !hasCallback)) { + if ( + name === 'queryStream' || + (name === 'query' && pkgName.includes('callback') && !hasCallback) + ) { let newResult = original.apply(this, arguments); ins.bindEmitter(newResult); diff --git a/test/instrumentation/modules/mariadb/mariadb.test.js b/test/instrumentation/modules/mariadb/mariadb.test.js index db41404cc22..fca41495341 100644 --- a/test/instrumentation/modules/mariadb/mariadb.test.js +++ b/test/instrumentation/modules/mariadb/mariadb.test.js @@ -59,11 +59,7 @@ var factories = [ ]; // var executors = ['execute']; // var executors = ['queryStream']; -var executors = [ - 'query', - 'execute', - // 'queryStream' -]; +var executors = ['query', 'execute', 'queryStream']; var universalArgumentSets = [ { @@ -101,66 +97,68 @@ factories.forEach(function (f) { var argumentSets = universalArgumentSets; if (executor === 'queryStream') { - // if (!['pool'].includes(type)) { - // t.test('streaming promise', function (t) { - // argumentSets.forEach(function (argumentSet) { - // var query = argumentSet.query; - // var names = argumentSet.names; - // var values = argumentSet.values; - // var name = `${type}.${executor}(${names.join(', ')})`; - // var args = values(query); - // t.test(name, function (t) { - // resetAgent(function (data) { - // console.log('RESET'); - // assertBasicQuery(t, query, data); - // t.end(); - // }); - // factory(function () { - // console.log('INIT TRANSACIONC'); - // agent.startTransaction('foo'); - // var stream = queryablePromise[executor].apply( - // queryablePromise, - // args, - // ); - // t.ok( - // agent.currentSpan === null, - // 'mariadb span should not spill into calling code', - // ); - // basicQueryStream(stream, t); - // }); - // }); - // }); - // }); - // executor = 'query'; - // t.test('streaming callback', function (t) { - // argumentSets.forEach(function (argumentSet) { - // var query = argumentSet.query; - // var names = argumentSet.names; - // var values = argumentSet.values; - // var name = `${type}.${executor}(${names.join(', ')})`; - // var args = values(query); - // t.test(name, function (t) { - // resetAgent(function (data) { - // console.log('RESET'); - // assertBasicQuery(t, query, data); - // t.end(); - // }); - // factory(function () { - // console.log('INIT TRANSACIONC'); - // agent.startTransaction('foo'); - // var stream = queryable[executor].apply(queryable, args); - // t.ok( - // agent.currentSpan === null, - // 'mariadb span should not spill into calling code', - // ); - // basicQueryStream(stream, t); - // }); - // }); - // }); - // }); - // } else { - // t.end(); - // } + if (!['pool'].includes(type)) { + t.test('streaming promise', function (t) { + argumentSets.forEach(function (argumentSet) { + var query = argumentSet.query; + var names = argumentSet.names; + var values = argumentSet.values; + var name = `${type}.${executor}(${names.join(', ')})`; + var args = values(query); + t.test(name, function (t) { + resetAgent(function (data) { + console.log('RESET'); + assertBasicQuery(t, query, data); + t.end(); + }); + factory(function () { + console.log('INIT TRANSACIONC'); + agent.startTransaction('foo'); + var stream = queryablePromise[executor].apply( + queryablePromise, + args, + ); + t.ok( + agent.currentSpan === null, + 'mariadb span should not spill into calling code', + ); + basicQueryStream(stream, t); + }); + }); + }); + }); + if (hasCallback) { + t.test('streaming callback', function (t) { + argumentSets.forEach(function (argumentSet) { + var query = argumentSet.query; + var names = argumentSet.names; + var values = argumentSet.values; + var name = `${type}.${executor}(${names.join(', ')})`; + var args = values(query); + t.test(name, function (t) { + resetAgent(function (data) { + console.log('RESET'); + assertBasicQuery(t, query, data); + t.end(); + }); + factory(function () { + console.log('INIT TRANSACIONC'); + agent.startTransaction('foo'); + var stream = queryable.query.apply(queryable, args); + console.log(stream); + t.ok( + agent.currentSpan === null, + 'mariadb span should not spill into calling code', + ); + basicQueryStream(stream, t); + }); + }); + }); + }); + } + } else { + t.end(); + } } else { if (hasCallback) { t.test('callback', function (t) { From 9f94e9e45bd13ce19fe6bc9f619577793cae75a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eneko=20Rodr=C3=ADguez?= Date: Tue, 28 May 2024 13:34:30 +0200 Subject: [PATCH 05/13] add batch and prepare --- lib/instrumentation/modules/mariadb.js | 122 +++++++--- .../modules/mariadb/mariadb.test.js | 230 +++++++++++++++--- 2 files changed, 286 insertions(+), 66 deletions(-) diff --git a/lib/instrumentation/modules/mariadb.js b/lib/instrumentation/modules/mariadb.js index df2be4b8e33..5ada8cff2ad 100644 --- a/lib/instrumentation/modules/mariadb.js +++ b/lib/instrumentation/modules/mariadb.js @@ -18,7 +18,6 @@ module.exports = function ( agent, { version, enabled, name: pkgName }, ) { - console.log('Starting mariadb instrumentation'); if (!enabled) { return mariadb; } @@ -43,16 +42,12 @@ module.exports = function ( function wrapPool(original) { return function wrappedPool() { - console.log('Inserting wrapPool'); let result = original.apply(this, arguments); - console.log('Result', result); shimmer.wrap( result, 'getConnection', function wrapGetConnection(...args) { - console.log('Test'); if (typeof arguments[0] === 'object') { - console.log('Cambiadno'); config = { ...defaultConfig, ...arguments[0], @@ -62,10 +57,8 @@ module.exports = function ( return wrapConnection.apply(result, args); }, ); - console.log('Result2', result); if (typeof arguments[0] === 'object') { - console.log('Cambiadno'); config = { ...defaultConfig, ...arguments[0], @@ -89,13 +82,15 @@ module.exports = function ( if (typeof result.queryStream === 'function') { shimmer.wrap(result, 'queryStream', wrapQuery); } + if (typeof result.batch === 'function') { + shimmer.wrap(result, 'batch', wrapQuery); + } return result; }; } function wrapAdd(original) { return function wrappedAdd() { - console.log('Executing wrapAdd', original, arguments); config = { ...defaultConfig, ...arguments[1], @@ -106,18 +101,14 @@ module.exports = function ( function wrapConnection(original) { return function wrappedConnection() { - console.log('Executing wrapConnection', pkgName, arguments); - if (typeof arguments[0] === 'object') { config = { ...defaultConfig, ...arguments[0], }; } - console.log(arguments); if (!pkgName.includes('callback')) { - console.log('Shimming promise'); return original.apply(this, arguments).then((res) => { if (typeof res.query === 'function') { shimmer.wrap(res, 'query', wrapQuery); @@ -130,21 +121,23 @@ module.exports = function ( shimmer.wrap(res, 'queryStream', wrapQuery); } + if (typeof res.batch === 'function') { + shimmer.wrap(res, 'batch', wrapQuery); + } + + if (typeof res.prepare === 'function') { + shimmer.wrap(res, 'prepare', wrapPrepare); + } + return Promise.resolve(res); }); } - console.log('test'); if (typeof arguments[arguments.length - 1] === 'function') { - console.log('Shimming callback', original); - return original.apply(this, [ ...[...arguments].slice(0, arguments.length - 1), (err, conn) => { - console.log('MI CALLBACK'); - console.log(err); if (err) return arguments[0](err); - console.log('test345'); if (typeof conn.query === 'function') { shimmer.wrap(conn, 'query', wrapQuery); @@ -156,13 +149,14 @@ module.exports = function ( if (typeof conn.queryStream === 'function') { shimmer.wrap(conn, 'queryStream', wrapQuery); } + if (typeof conn.batch === 'function') { + shimmer.wrap(conn, 'batch', wrapQuery); + } return arguments[0](err, conn); }, ]); } else { - console.log('Shimming WITHOUT callback'); - let result = original.apply(this, arguments); if (typeof result.query === 'function') { @@ -175,16 +169,16 @@ module.exports = function ( if (typeof result.queryStream === 'function') { shimmer.wrap(result, 'queryStream', wrapQuery); } + if (typeof result.batch === 'function') { + shimmer.wrap(result, 'batch', wrapQuery); + } return result; } }; } function wrapQuery(original, name) { - console.log('Inserting wrapQuery', original); return function wrappedQuery(sql, values, cb) { - console.log('Executing wrapQuery', arguments); - agent.logger.debug('intercepted call to mariadb.%s', original.name); var span = ins.createSpan(null, 'db', 'mariadb', 'query', { exitSpan: true, @@ -197,7 +191,6 @@ module.exports = function ( const wrapCallback = function (origCallback) { hasCallback = true; return ins.bindFunction(function wrappedCallback(_err) { - console.log('CAllbkac wrappeed', arguments); span.end(); return origCallback.apply(this, arguments); }); @@ -244,17 +237,12 @@ module.exports = function ( name !== 'queryStream' && (name !== 'query' || !pkgName.includes('callback')) ) { - console.log('Ejecuting promise'); return new Promise(async (resolve, reject) => { - console.log('test1'); let awaitedResult = await original.apply(this, arguments); - console.log('test2'); span.end(); return resolve(awaitedResult); }); } - console.log('Devolviendo normal sin '); - console.log(arguments); if ( name === 'queryStream' || @@ -265,7 +253,6 @@ module.exports = function ( ins.bindEmitter(newResult); shimmer.wrap(newResult, 'emit', function (origEmit) { return function (event) { - console.log('Evento de callback', event, arguments); switch (event) { case 'error': case 'end': @@ -280,4 +267,79 @@ module.exports = function ( } }; } + function wrapPrepare(original, name) { + return function wrappedPrepare(sql) { + return original.apply(this, arguments).then((newResult) => { + function wrapPreparedExecute(original, name) { + return function wrappedQuery(values) { + agent.logger.debug('intercepted call to mariadb.%s', original.name); + var span = ins.createSpan(null, 'db', 'mariadb', 'query', { + exitSpan: true, + }); + if (!span) { + return original.apply(this, arguments); + } + + let host, port, user, database; + if (typeof config === 'object') { + ({ host, port, user, database } = config); + } + + span._setDestinationContext(getDBDestination(host, port)); + let sqlStr; + switch (typeof sql) { + case 'string': + sqlStr = sql; + break; + case 'object': + sqlStr = sql.sql; + break; + case 'function': + arguments[0] = wrapCallback(sql); + break; + } + + if (sqlStr) { + span.setDbContext({ + type: 'sql', + instance: database, + user, + statement: sqlStr, + }); + span.name = sqlSummary(sqlStr); + } else { + span.setDbContext({ type: 'sql', instance: database, user }); + } + + if (name === 'executeStream') { + let newResult = original.apply(this, arguments); + + ins.bindEmitter(newResult); + shimmer.wrap(newResult, 'emit', function (origEmit) { + return function (event) { + switch (event) { + case 'error': + case 'end': + span.end(); + } + return origEmit.apply(this, arguments); + }; + }); + return newResult; + } else { + return new Promise(async (resolve, reject) => { + let awaitedResult = await original.apply(this, arguments); + span.end(); + return resolve(awaitedResult); + }); + } + }; + } + shimmer.wrap(newResult, 'execute', wrapPreparedExecute); + shimmer.wrap(newResult, 'executeStream', wrapPreparedExecute); + + return newResult; + }); + }; + } }; diff --git a/test/instrumentation/modules/mariadb/mariadb.test.js b/test/instrumentation/modules/mariadb/mariadb.test.js index fca41495341..f4bada252ef 100644 --- a/test/instrumentation/modules/mariadb/mariadb.test.js +++ b/test/instrumentation/modules/mariadb/mariadb.test.js @@ -59,7 +59,7 @@ var factories = [ ]; // var executors = ['execute']; // var executors = ['queryStream']; -var executors = ['query', 'execute', 'queryStream']; +var executors = ['query', 'execute', 'queryStream', 'batch', 'prepare']; var universalArgumentSets = [ { @@ -84,6 +84,19 @@ var universalArgumentSets = [ }, ]; +var batchArgumentSets = [ + { + names: ['sql', 'values'], + query: 'INSERT INTO users (name) VALUES (?)', + values: (query, cb) => [query, [['test'], ['test2']], cb], + }, + { + names: ['options', 'values'], + query: 'INSERT INTO users (name) VALUES (?)', + values: (query, cb) => [{ sql: query }, [['test'], ['test2']], cb], + }, +]; + factories.forEach(function (f) { var factory = f[0]; var type = f[1]; @@ -94,7 +107,8 @@ factories.forEach(function (f) { executors.forEach(function (executor) { t.test(executor, function (t) { var isQuery = executor === 'query'; - var argumentSets = universalArgumentSets; + var argumentSets = + executor === 'batch' ? batchArgumentSets : universalArgumentSets; if (executor === 'queryStream') { if (!['pool'].includes(type)) { @@ -107,12 +121,10 @@ factories.forEach(function (f) { var args = values(query); t.test(name, function (t) { resetAgent(function (data) { - console.log('RESET'); assertBasicQuery(t, query, data); t.end(); }); factory(function () { - console.log('INIT TRANSACIONC'); agent.startTransaction('foo'); var stream = queryablePromise[executor].apply( queryablePromise, @@ -137,15 +149,12 @@ factories.forEach(function (f) { var args = values(query); t.test(name, function (t) { resetAgent(function (data) { - console.log('RESET'); assertBasicQuery(t, query, data); t.end(); }); factory(function () { - console.log('INIT TRANSACIONC'); agent.startTransaction('foo'); var stream = queryable.query.apply(queryable, args); - console.log(stream); t.ok( agent.currentSpan === null, 'mariadb span should not spill into calling code', @@ -159,6 +168,69 @@ factories.forEach(function (f) { } else { t.end(); } + } else if (executor === 'prepare') { + if (!['pool'].includes(type)) { + t.test('prepare', function (t) { + argumentSets.forEach(function (argumentSet) { + var query = argumentSet.query; + var names = argumentSet.names; + var values = argumentSet.values; + var name = `${type}.${executor}(${names.join(', ')})`; + var args = values(query); + t.test(name, function (t) { + resetAgent(function (data) { + assertBasicQuery(t, query, data); + t.end(); + }); + factory(function () { + agent.startTransaction('foo'); + queryablePromise[executor] + .apply(queryablePromise, [args[0]]) + .then((preparedStatement) => { + var result = preparedStatement.execute(args[1]); + + t.ok( + agent.currentSpan === null, + 'mariadb span should not spill into calling code', + ); + basicQueryPromise(t, result); + }); + }); + }); + }); + }); + t.test('prepare stream', function (t) { + argumentSets.forEach(function (argumentSet) { + var query = argumentSet.query; + var names = argumentSet.names; + var values = argumentSet.values; + var name = `${type}.${executor}(${names.join(', ')})`; + var args = values(query); + t.test(name, function (t) { + resetAgent(function (data) { + assertBasicQuery(t, query, data); + t.end(); + }); + factory(function () { + agent.startTransaction('foo'); + queryablePromise[executor] + .apply(queryablePromise, [args[0]]) + .then((preparedStatement) => { + var result = preparedStatement.executeStream(args[1]); + + t.ok( + agent.currentSpan === null, + 'mariadb span should not spill into calling code', + ); + basicQueryStream(result, t); + }); + }); + }); + }); + }); + } else { + t.end(); + } } else { if (hasCallback) { t.test('callback', function (t) { @@ -167,15 +239,22 @@ factories.forEach(function (f) { var names = argumentSet.names; var values = argumentSet.values; var name = `${type}.${executor}(${names.join(', ')}, callback)`; - var args = values(query, basicQueryCallback(t)); + var args = + executor === 'batch' + ? values(query, batchQueryCallback(t)) + : values(query, basicQueryCallback(t)); t.test(name, function (t) { resetAgent(function (data) { - assertBasicQuery(t, query, data); + assertBasicQuery( + t, + query, + data, + executor === 'batch' ? 'INSERT INTO users' : 'SELECT', + ); t.end(); }); factory(function () { agent.startTransaction('foo'); - console.log('EMPEZANDO'); queryable[executor].apply(queryable, args); t.ok( agent.currentSpan === null, @@ -187,7 +266,7 @@ factories.forEach(function (f) { }); } t.test('promise', function (t) { - universalArgumentSets.forEach(function (argumentSet) { + argumentSets.forEach(function (argumentSet) { var query = argumentSet.query; var names = argumentSet.names; var values = argumentSet.values; @@ -195,7 +274,12 @@ factories.forEach(function (f) { var args = values(query); t.test(name, function (t) { resetAgent(function (data) { - assertBasicQuery(t, query, data); + assertBasicQuery( + t, + query, + data, + executor === 'batch' ? 'INSERT INTO users' : 'SELECT', + ); t.end(); }); factory(function () { @@ -208,7 +292,11 @@ factories.forEach(function (f) { agent.currentSpan === null, 'mariadb span should not spill into calling code', ); - basicQueryPromise(t, promise); + if (executor === 'batch') { + batchQueryPromise(t, promise); + } else { + basicQueryPromise(t, promise); + } }); }); }); @@ -417,7 +505,6 @@ function basicQueryPromise(t, p) { p.then( function (response) { - console.log('Ejecutada'); var rows = response[0]; t.strictEqual(rows.solution, 2); done(); @@ -428,10 +515,26 @@ function basicQueryPromise(t, p) { }, ); } +function batchQueryPromise(t, p) { + function done() { + agent.endTransaction(); + } + + p.then( + function (response) { + var rows = response; + t.strictEqual(rows.affectedRows, 2); + done(); + }, + function (error) { + t.error(error); + done(); + }, + ); +} function basicQueryCallback(t) { return function (err, rows, fields) { - console.log('CALLBACK'); t.ok( agent.currentSpan === null, 'mariadb span should not spill into calling code', @@ -441,11 +544,21 @@ function basicQueryCallback(t) { agent.endTransaction(); }; } +function batchQueryCallback(t) { + return function (err, rows, fields) { + t.ok( + agent.currentSpan === null, + 'mariadb span should not spill into calling code', + ); + t.error(err); + t.strictEqual(rows.affectedRows, 2); + agent.endTransaction(); + }; +} function basicQueryStream(stream, t) { var results = 0; stream.on('error', function (err) { - console.log('ERROR', err); t.ok( agent.currentSpan === null, 'mariadb span should not be active in user code', @@ -453,9 +566,6 @@ function basicQueryStream(stream, t) { t.error(err); }); stream.on('data', function (row) { - console.log(agent.currentSpan); - - console.log('DATA'); t.ok( agent.currentSpan === null, 'mariadb span should not be active in user code', @@ -464,19 +574,16 @@ function basicQueryStream(stream, t) { t.strictEqual(row.solution, 2); }); stream.on('end', function () { - console.log('END'); - t.ok( agent.currentSpan === null, 'mariadb span should not be active in user code', ); t.strictEqual(results, 1); - console.log('testes', results); agent.endTransaction(); }); } -function assertBasicQuery(t, sql, data) { +function assertBasicQuery(t, sql, data, spanName = 'SELECT') { t.strictEqual(data.transactions.length, 1); t.strictEqual(data.spans.length, 1); @@ -484,11 +591,11 @@ function assertBasicQuery(t, sql, data) { var span = data.spans[0]; t.strictEqual(trans.name, 'foo'); - assertSpan(t, span, sql); + assertSpan(t, span, sql, spanName); } -function assertSpan(t, span, sql) { - t.strictEqual(span.name, 'SELECT', 'span.name'); +function assertSpan(t, span, sql, spanName = 'SELECT') { + t.strictEqual(span.name, spanName, 'span.name'); t.strictEqual(span.type, 'db', 'span.type'); t.strictEqual(span.subtype, 'mariadb', 'span.subtype'); t.strictEqual(span.action, 'query', 'span.action'); @@ -528,7 +635,6 @@ function assertSpan(t, span, sql) { function createConnection(cb) { setup(function () { _teardown = function teardown() { - console.log('CERRANDO'); if (queryable) { queryable.end(); queryable = undefined; @@ -544,10 +650,18 @@ function createConnection(cb) { queryable.connect((err) => { if (err) throw err; - mariadbPromise.createConnection(connectionOptions).then(function (conn2) { - queryablePromise = conn2; - cb(); - }); + mariadbPromise + .createConnection(connectionOptions) + .then(async function (conn2) { + queryablePromise = conn2; + + await queryablePromise.execute('DROP TABLE IF EXISTS users'); + + await queryablePromise.execute( + 'CREATE TABLE users (id INTEGER PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255))', + ); + cb(); + }); }); }); } @@ -568,7 +682,15 @@ function createPool(cb) { queryable = mariadb.createPool(connectionOptions); queryablePromise = mariadbPromise.createPool(connectionOptions); - cb(); + queryablePromise.execute('DROP TABLE IF EXISTS users').then(() => { + queryablePromise + .execute( + 'CREATE TABLE users (id INTEGER PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255))', + ) + .then(() => { + cb(); + }); + }); }); } @@ -598,7 +720,16 @@ function createPoolAndGetConnection(cb) { poolPromise.getConnection().then(function (conn) { queryablePromise = conn; - cb(); + + queryablePromise.execute('DROP TABLE IF EXISTS users').then(() => { + queryablePromise + .execute( + 'CREATE TABLE users (id INTEGER PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255))', + ) + .then(() => { + cb(); + }); + }); }); }); }); @@ -633,7 +764,16 @@ function createPoolClusterAndGetConnection(cb) { clusterPromise.getConnection().then(function (conn2) { queryablePromise = conn2; - cb(); + + queryablePromise.execute('DROP TABLE IF EXISTS users').then(() => { + queryablePromise + .execute( + 'CREATE TABLE users (id INTEGER PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255))', + ) + .then(() => { + cb(); + }); + }); }); }); }); @@ -657,7 +797,16 @@ function createPoolClusterAndGetConnectionViaOf(cb) { .getConnection() .then((conn) => { queryablePromise = conn; - cb(); + + queryablePromise.execute('DROP TABLE IF EXISTS users').then(() => { + queryablePromise + .execute( + 'CREATE TABLE users (id INTEGER PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255))', + ) + .then(() => { + cb(); + }); + }); }); }); } @@ -676,7 +825,16 @@ function createPoolClusterAndGetConnectionViaOfDirect(cb) { clusterPromise.add('master-test', connectionOptions); clusterPromise.getConnection('.*', 'RANDOM').then((conn) => { queryablePromise = conn; - cb(); + + queryablePromise.execute('DROP TABLE IF EXISTS users').then(() => { + queryablePromise + .execute( + 'CREATE TABLE users (id INTEGER PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255))', + ) + .then(() => { + cb(); + }); + }); }); }); } From e971246f177695594c21e6770418ab890fcd938e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eneko=20Rodr=C3=ADguez?= Date: Tue, 28 May 2024 13:46:34 +0200 Subject: [PATCH 06/13] update last test --- .../instrumentation/modules/mariadb/_utils.js | 6 +- .../modules/mariadb/mariadb.test.js | 4 +- .../modules/mariadb/minitest-cb.test.js | 64 ------------------- .../modules/mariadb/minitest-cluster.test.js | 50 --------------- .../modules/mariadb/minitest.test.js | 57 ----------------- .../modules/mariadb/pool-release-1.test.js | 12 ++-- 6 files changed, 13 insertions(+), 180 deletions(-) delete mode 100644 test/instrumentation/modules/mariadb/minitest-cb.test.js delete mode 100644 test/instrumentation/modules/mariadb/minitest-cluster.test.js delete mode 100644 test/instrumentation/modules/mariadb/minitest.test.js diff --git a/test/instrumentation/modules/mariadb/_utils.js b/test/instrumentation/modules/mariadb/_utils.js index 63264a176c2..e68efa5746c 100644 --- a/test/instrumentation/modules/mariadb/_utils.js +++ b/test/instrumentation/modules/mariadb/_utils.js @@ -6,7 +6,7 @@ 'use strict'; -var mysql = require('mysql2'); +var mariadb = require('mariadb/callback'); exports.reset = reset; exports.credentials = credentials; @@ -23,7 +23,9 @@ function credentials(conf) { } function reset(cb) { - var client = mysql.createConnection(credentials({ database: 'mysql' })); + var client = mariadb.createConnection( + credentials({ database: 'test_elastic_apm' }), + ); client.connect(function (err) { if (err) throw err; diff --git a/test/instrumentation/modules/mariadb/mariadb.test.js b/test/instrumentation/modules/mariadb/mariadb.test.js index f4bada252ef..2996f82aa98 100644 --- a/test/instrumentation/modules/mariadb/mariadb.test.js +++ b/test/instrumentation/modules/mariadb/mariadb.test.js @@ -14,9 +14,9 @@ if (process.env.GITHUB_ACTIONS === 'true' && process.platform === 'win32') { const semver = require('semver'); const { safeGetPackageVersion } = require('../../../_utils'); const mariadbVer = safeGetPackageVersion('mariadb'); -if (semver.gte(mariadbVer, '3.0.0') && semver.lt(process.version, '14.6.0')) { +if (semver.gte(mariadbVer, '3.0.0') && semver.lt(process.version, '14.0.0')) { console.log( - `# SKIP mysql2@${mariadbVer} does not support node ${process.version}`, + `# SKIP mariadb@${mariadbVer} does not support node ${process.version}`, ); process.exit(); } diff --git a/test/instrumentation/modules/mariadb/minitest-cb.test.js b/test/instrumentation/modules/mariadb/minitest-cb.test.js deleted file mode 100644 index 39149e4ced6..00000000000 --- a/test/instrumentation/modules/mariadb/minitest-cb.test.js +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and other contributors where applicable. - * Licensed under the BSD 2-Clause License; you may not use this file except in - * compliance with the BSD 2-Clause License. - */ - -var agent = require('../../../..').start({ - secretToken: 'test', - serviceName: 'test-mariadb-integration', - serverUrl: 'https://apm-server-lo6wr9-pre.mm-red.net', - environment: 'test', - captureExceptions: false, - metricsInterval: 0, - centralConfig: false, - spanCompressionEnabled: false, -}); - -const mariadb = require('mariadb/callback'); - -function asyncFunction() { - let pool; - try { - pool = mariadb.createPool({ - host: 'mariadb.test.orb.local', - user: 'root', - connectionLimit: 5, - }); - - pool.getConnection(function (err, conn) { - if (err) throw err; - conn.query('SELECT 1 + ? as val', [1], function (err, rows, fields) { - conn.release(); - if (err) throw err; - // rows: [ {val: 1}, meta: ... ] - console.log(rows); - // const res = await conn.query('INSERT INTO myTable value (?, ?)', [ - // 1, - // 'mariadb', - // ]); - // res: { affectedRows: 1, insertId: 1, warningStatus: 0 } - agent.endTransaction('foo'); - pool.end(); - }); - }); - - // agent.startTransaction('foo'); - // pool.query('SELECT 1 + ? as val', [1], function (err, rows, fields) { - // pool.end(); - // if (err) throw err; - // // rows: [ {val: 1}, meta: ... ] - // console.log(rows); - // // const res = await conn.query('INSERT INTO myTable value (?, ?)', [ - // // 1, - // // 'mariadb', - // // ]); - // // res: { affectedRows: 1, insertId: 1, warningStatus: 0 } - // agent.endTransaction('foo'); - // }); - } finally { - // if (pool) pool.end(); //release to pool - } -} - -asyncFunction(); diff --git a/test/instrumentation/modules/mariadb/minitest-cluster.test.js b/test/instrumentation/modules/mariadb/minitest-cluster.test.js deleted file mode 100644 index 4963306b51d..00000000000 --- a/test/instrumentation/modules/mariadb/minitest-cluster.test.js +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and other contributors where applicable. - * Licensed under the BSD 2-Clause License; you may not use this file except in - * compliance with the BSD 2-Clause License. - */ - -(async function () { - var agent = require('../../../..').start({ - secretToken: 'test', - serviceName: 'test-mariadb-integration', - serverUrl: 'https://apm-server-lo6wr9-pre.mm-red.net', - environment: 'test', - captureExceptions: false, - metricsInterval: 0, - centralConfig: false, - spanCompressionEnabled: false, - }); - - const mariadb = require('mariadb'); - - const pool = mariadb.createPoolCluster(); - - async function asyncFunction() { - let conn; - try { - pool.add('master', { - host: 'mariadb.test.orb.local', - user: 'root', - connectionLimit: 5, - }); - agent.startTransaction('foo'); - conn = await pool.getConnection(); - - const rows = await conn.query('SELECT 1 + ? as val', [1]); - // rows: [ {val: 1}, meta: ... ] - console.log(rows); - // const res = await conn.query('INSERT INTO myTable value (?, ?)', [ - // 1, - // 'mariadb', - // ]); - // res: { affectedRows: 1, insertId: 1, warningStatus: 0 } - agent.endTransaction('foo'); - } finally { - if (conn) conn.end(); //release to pool - } - } - - await asyncFunction(); - await pool.end(); -})(); diff --git a/test/instrumentation/modules/mariadb/minitest.test.js b/test/instrumentation/modules/mariadb/minitest.test.js deleted file mode 100644 index 0e1831f79e9..00000000000 --- a/test/instrumentation/modules/mariadb/minitest.test.js +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and other contributors where applicable. - * Licensed under the BSD 2-Clause License; you may not use this file except in - * compliance with the BSD 2-Clause License. - */ - -(async function () { - var agent = require('../../../..').start({ - secretToken: 'test', - serviceName: 'test-mariadb-integration', - serverUrl: 'https://apm-server-lo6wr9-pre.mm-red.net', - environment: 'test', - captureExceptions: false, - metricsInterval: 0, - centralConfig: false, - spanCompressionEnabled: false, - }); - - const mariadb = require('mariadb'); - - const pool = mariadb.createPool({ - host: 'mariadb.test.orb.local', - user: 'root', - connectionLimit: 5, - }); - - async function asyncFunction() { - let conn; - try { - agent.startTransaction('foo'); - conn = await mariadb.createConnection({ - host: 'mariadb.test.orb.local', - user: 'root', - connectionLimit: 5, - }); - - const rows = await conn.queryStream('SELECT 1 + ? as val', [1]); - - for await (const row of rows) { - console.log(row); - } - // rows: [ {val: 1}, meta: ... ] - // console.log(rows); - // const res = await conn.query('INSERT INTO myTable value (?, ?)', [ - // 1, - // 'mariadb', - // ]); - // res: { affectedRows: 1, insertId: 1, warningStatus: 0 } - agent.endTransaction('foo'); - } finally { - if (conn) conn.end(); //release to pool - } - } - - await asyncFunction(); - await pool.end(); -})(); diff --git a/test/instrumentation/modules/mariadb/pool-release-1.test.js b/test/instrumentation/modules/mariadb/pool-release-1.test.js index 7475e995fa9..2edf07ccbd4 100644 --- a/test/instrumentation/modules/mariadb/pool-release-1.test.js +++ b/test/instrumentation/modules/mariadb/pool-release-1.test.js @@ -13,10 +13,10 @@ if (process.env.GITHUB_ACTIONS === 'true' && process.platform === 'win32') { const semver = require('semver'); const { safeGetPackageVersion } = require('../../../_utils'); -const mysql2Ver = safeGetPackageVersion('mysql2'); -if (semver.gte(mysql2Ver, '3.0.0') && semver.lt(process.version, '14.6.0')) { +const mariadbVer = safeGetPackageVersion('mariadb'); +if (semver.gte(mariadbVer, '3.0.0') && semver.lt(process.version, '14.0.0')) { console.log( - `# SKIP mysql2@${mysql2Ver} does not support node ${process.version}`, + `# SKIP mariadb@${mariadbVer} does not support node ${process.version}`, ); process.exit(); } @@ -29,7 +29,7 @@ var agent = require('../../../..').start({ centralConfig: false, }); -var mysql = require('mysql2'); +var mariadb = require('mariadb/callback'); var test = require('tape'); var utils = require('./_utils'); @@ -38,7 +38,9 @@ test('release connection prior to transaction', function (t) { createPool(function (pool) { pool.getConnection(function (err, conn) { t.error(err); + console.log('Test1'); conn.release(); // important to release connection before starting the transaction + console.log('Test2'); agent.startTransaction('foo'); t.ok(agent.currentTransaction); @@ -55,7 +57,7 @@ test('release connection prior to transaction', function (t) { function createPool(cb) { setup(function () { - cb(mysql.createPool(utils.credentials())); + cb(mariadb.createPool(utils.credentials())); }); } From 90ed7dd87f55dae32f7c05493b6a275f3aa78962 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eneko=20Rodr=C3=ADguez?= Date: Tue, 28 May 2024 13:52:03 +0200 Subject: [PATCH 07/13] add to tav --- .ci/tav.json | 1 + .tav.yml | 7 +++++++ docs/supported-technologies.asciidoc | 1 + package.json | 2 +- 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.ci/tav.json b/.ci/tav.json index 8ffb8edce08..ebc48ee4d6a 100644 --- a/.ci/tav.json +++ b/.ci/tav.json @@ -31,6 +31,7 @@ { "name": "mongodb-core", "minMajorVersion": 8 }, { "name": "mysql", "minMajorVersion": 8 }, { "name": "mysql2", "minMajorVersion": 8 }, + { "name": "mariadb", "minMajorVersion": 14 }, { "name": "next", "minMajorVersion": 14 }, { "name": "pg", "minMajorVersion": 8 }, { "name": "redis", "minMajorVersion": 8 }, diff --git a/.tav.yml b/.tav.yml index 7fd1ae6e54a..13fb94ee1c3 100644 --- a/.tav.yml +++ b/.tav.yml @@ -54,6 +54,13 @@ mysql2: - node test/instrumentation/modules/mysql2/mysql.test.js - node test/instrumentation/modules/mysql2/pool-release-1.test.js +mariadb: + - versions: '>=3.3.0 <4' + node: '>=14.0.0' + commands: + - node test/instrumentation/modules/mariadb/mariadb.test.js + - node test/instrumentation/modules/mariadb/pool-release-1.test.js + redis: - versions: '>=2.0.0 <4.0.0' commands: node test/instrumentation/modules/redis-2-3.test.js diff --git a/docs/supported-technologies.asciidoc b/docs/supported-technologies.asciidoc index 5461eaec9bf..aa76967cf34 100644 --- a/docs/supported-technologies.asciidoc +++ b/docs/supported-technologies.asciidoc @@ -137,6 +137,7 @@ so those should be supported as well. |https://www.npmjs.com/package/mongoose[mongoose] |>=5.7.0 <8 |Supported via mongodb |https://www.npmjs.com/package/mysql[mysql] |^2.0.0 |Will instrument all queries |https://www.npmjs.com/package/mysql2[mysql2] |>=1.0.0 <4.0.0 |Will instrument all queries +|https://www.npmjs.com/package/mariadb[mariadb] |>=3.3.0 <4.0.0 |Will instrument all queries |https://www.npmjs.com/package/pg[pg] |>=4.0.0 <9.0.0 |Will instrument all queries |https://www.npmjs.com/package/redis[redis] |>=2.0.0 <5.0.0 |Will instrument all queries |https://www.npmjs.com/package/tedious[tedious] |>=1.9 <19.0.0 | (Excluding v4.0.0.) Will instrument all queries diff --git a/package.json b/package.json index 66d57941f84..1c085e4cf89 100644 --- a/package.json +++ b/package.json @@ -215,4 +215,4 @@ "wait-on": "^7.0.1", "ws": "^7.2.1" } -} +} \ No newline at end of file From 79881fbb669efdc4b87865bba5097fab56743822 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eneko=20Rodr=C3=ADguez?= Date: Tue, 28 May 2024 13:54:44 +0200 Subject: [PATCH 08/13] format --- lib/instrumentation/modules/mariadb.js | 14 ++++---------- .../modules/mariadb/mariadb.test.js | 4 +--- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/lib/instrumentation/modules/mariadb.js b/lib/instrumentation/modules/mariadb.js index 5ada8cff2ad..e2e9b94ef69 100644 --- a/lib/instrumentation/modules/mariadb.js +++ b/lib/instrumentation/modules/mariadb.js @@ -10,7 +10,6 @@ var semver = require('semver'); var sqlSummary = require('sql-summary'); var shimmer = require('../shimmer'); -var symbols = require('../../symbols'); var { getDBDestination } = require('../context'); module.exports = function ( @@ -237,10 +236,9 @@ module.exports = function ( name !== 'queryStream' && (name !== 'query' || !pkgName.includes('callback')) ) { - return new Promise(async (resolve, reject) => { - let awaitedResult = await original.apply(this, arguments); + return original.apply(this, arguments).then((awaitedResult) => { span.end(); - return resolve(awaitedResult); + return awaitedResult; }); } @@ -294,9 +292,6 @@ module.exports = function ( case 'object': sqlStr = sql.sql; break; - case 'function': - arguments[0] = wrapCallback(sql); - break; } if (sqlStr) { @@ -327,10 +322,9 @@ module.exports = function ( }); return newResult; } else { - return new Promise(async (resolve, reject) => { - let awaitedResult = await original.apply(this, arguments); + return original.apply(this, arguments).then((awaitedResult) => { span.end(); - return resolve(awaitedResult); + return awaitedResult; }); } }; diff --git a/test/instrumentation/modules/mariadb/mariadb.test.js b/test/instrumentation/modules/mariadb/mariadb.test.js index 2996f82aa98..a91b552545a 100644 --- a/test/instrumentation/modules/mariadb/mariadb.test.js +++ b/test/instrumentation/modules/mariadb/mariadb.test.js @@ -36,7 +36,6 @@ var test = require('tape'); var utils = require('./_utils'); var mockClient = require('../../../_mock_http_client'); -var findObjInArray = require('../../../_utils').findObjInArray; var connectionOptions = utils.credentials(); var queryable; @@ -106,7 +105,6 @@ factories.forEach(function (f) { t.on('end', teardown); executors.forEach(function (executor) { t.test(executor, function (t) { - var isQuery = executor === 'query'; var argumentSets = executor === 'batch' ? batchArgumentSets : universalArgumentSets; @@ -845,7 +843,7 @@ function setup(cb) { } // placeholder variable to hold the teardown function created by the setup function -var _teardown = function () { }; +var _teardown = function () {}; var teardown = function () { _teardown(); }; From 073e9a66c001744464cdd2a7154ed50a95657833 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eneko=20Rodr=C3=ADguez?= Date: Tue, 28 May 2024 14:02:22 +0200 Subject: [PATCH 09/13] add changelog --- CHANGELOG.asciidoc | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 040814c4d02..3b2356bc89b 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -33,6 +33,21 @@ Notes: See the <> guide. +==== Unreleased + +[float] +===== Breaking changes + +[float] +===== Features +Add support for mariadb >=3.3.0 instrumentation + +[float] +===== Bug fixes + +[float] +===== Chores + [[release-notes-4.5.4]] ==== 4.5.4 - 2024/05/13 From ee6cc66d6ec989562eaff878d5a03bd34fd2e831 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eneko=20Rodr=C3=ADguez?= Date: Tue, 28 May 2024 14:24:16 +0200 Subject: [PATCH 10/13] test: add missing tests --- .../modules/mariadb/mariadb.test.js | 371 +++++++++--------- 1 file changed, 179 insertions(+), 192 deletions(-) diff --git a/test/instrumentation/modules/mariadb/mariadb.test.js b/test/instrumentation/modules/mariadb/mariadb.test.js index a91b552545a..2a5b088c1f9 100644 --- a/test/instrumentation/modules/mariadb/mariadb.test.js +++ b/test/instrumentation/modules/mariadb/mariadb.test.js @@ -12,7 +12,7 @@ if (process.env.GITHUB_ACTIONS === 'true' && process.platform === 'win32') { } const semver = require('semver'); -const { safeGetPackageVersion } = require('../../../_utils'); +const { safeGetPackageVersion, findObjInArray } = require('../../../_utils'); const mariadbVer = safeGetPackageVersion('mariadb'); if (semver.gte(mariadbVer, '3.0.0') && semver.lt(process.version, '14.0.0')) { console.log( @@ -303,196 +303,182 @@ factories.forEach(function (f) { }); }); - // t.test('simultaneous queries', function (t) { - // t.test('on same connection', function (t) { - // resetAgent(4, function (data) { - // t.strictEqual(data.transactions.length, 1); - // t.strictEqual(data.spans.length, 3); - - // var trans = data.transactions[0]; - - // t.strictEqual(trans.name, 'foo'); - - // data.spans.forEach(function (span) { - // assertSpan(t, span, sql); - // t.equal( - // span.parent_id, - // trans.id, - // 'each mysql2 span is a child of the transaction', - // ); - // }); - - // t.end(); - // }); - - // var sql = 'SELECT 1 + ? AS solution'; - - // factory(function () { - // var n = 0; - // var trans = agent.startTransaction('foo'); - - // queryable.query(sql, [1], function (err, rows, fields) { - // t.error(err); - // t.strictEqual(rows[0].solution, 2); - // if (++n === 3) done(); - // }); - // queryable.query(sql, [2], function (err, rows, fields) { - // t.error(err); - // t.strictEqual(rows[0].solution, 3); - // if (++n === 3) done(); - // }); - // queryable.query(sql, [3], function (err, rows, fields) { - // t.error(err); - // t.strictEqual(rows[0].solution, 4); - // if (++n === 3) done(); - // }); - - // function done() { - // trans.end(); - // } - // }); - // }); - - // t.test('on different connections', function (t) { - // resetAgent(4, function (data) { - // t.strictEqual(data.transactions.length, 1); - // t.strictEqual(data.spans.length, 3); - - // var trans = data.transactions[0]; - - // t.strictEqual(trans.name, 'foo'); - - // data.spans.forEach(function (span) { - // assertSpan(t, span, sql); - // t.equal( - // span.parent_id, - // trans.id, - // 'each mysql2 span is a child of the transaction', - // ); - // }); - - // t.end(); - // }); - - // var sql = 'SELECT 1 + ? AS solution'; - - // createPool(function () { - // var n = 0; - // var trans = agent.startTransaction('foo'); - - // queryable.getConnection(function (err, conn) { - // t.error(err); - // conn.query(sql, [1], function (err, rows, fields) { - // t.error(err); - // t.strictEqual(rows[0].solution, 2); - // if (++n === 3) done(); - // }); - // }); - // queryable.getConnection(function (err, conn) { - // t.error(err); - // conn.query(sql, [2], function (err, rows, fields) { - // t.error(err); - // t.strictEqual(rows[0].solution, 3); - // if (++n === 3) done(); - // }); - // }); - // queryable.getConnection(function (err, conn) { - // t.error(err); - // conn.query(sql, [3], function (err, rows, fields) { - // t.error(err); - // t.strictEqual(rows[0].solution, 4); - // if (++n === 3) done(); - // }); - // }); - - // function done() { - // trans.end(); - // } - // }); - // }); - // }); - - // t.test('simultaneous transactions', function (t) { - // resetAgent(6, function (data) { - // t.strictEqual(data.transactions.length, 3); - // t.strictEqual(data.spans.length, 3); - // var names = data.transactions - // .map(function (trans) { - // return trans.name; - // }) - // .sort(); - // t.deepEqual(names, ['bar', 'baz', 'foo']); - - // data.transactions.forEach(function (trans) { - // const span = findObjInArray(data.spans, 'transaction_id', trans.id); - // t.ok(span, 'transaction should have span'); - // assertSpan(t, span, sql); - // }); - - // t.end(); - // }); - - // var sql = 'SELECT 1 + ? AS solution'; - - // factory(function () { - // setImmediate(function () { - // var trans = agent.startTransaction('foo'); - // queryable.query(sql, [1], function (err, rows, fields) { - // t.error(err); - // t.strictEqual(rows[0].solution, 2); - // trans.end(); - // }); - // }); - - // setImmediate(function () { - // var trans = agent.startTransaction('bar'); - // queryable.query(sql, [2], function (err, rows, fields) { - // t.error(err); - // t.strictEqual(rows[0].solution, 3); - // trans.end(); - // }); - // }); - - // setImmediate(function () { - // var trans = agent.startTransaction('baz'); - // queryable.query(sql, [3], function (err, rows, fields) { - // t.error(err); - // t.strictEqual(rows[0].solution, 4); - // trans.end(); - // }); - // }); - // }); - // }); - - // // Only pools have a getConnection function - // if (type === 'pool') { - // t.test('connection.release()', function (t) { - // resetAgent(function (data) { - // assertBasicQuery(t, sql, data); - // t.end(); - // }); - - // var sql = 'SELECT 1 + 1 AS solution'; - - // factory(function () { - // agent.startTransaction('foo'); - - // queryable.getConnection(function (err, conn) { - // t.error(err); - // conn.release(); - - // queryable.getConnection(function (err, conn) { - // t.error(err); - // conn.query(sql, basicQueryCallback(t)); - // t.ok( - // agent.currentSpan === null, - // 'mysql2 span should not spill into calling code', - // ); - // }); - // }); - // }); - // }); - // } + t.test('simultaneous queries', function (t) { + t.test('on same connection', function (t) { + resetAgent(4, function (data) { + t.strictEqual(data.transactions.length, 1); + t.strictEqual(data.spans.length, 3); + + var trans = data.transactions[0]; + + t.strictEqual(trans.name, 'foo'); + + data.spans.forEach(function (span) { + assertSpan(t, span, sql); + t.equal( + span.parent_id, + trans.id, + 'each mariadb span is a child of the transaction', + ); + }); + + t.end(); + }); + + var sql = 'SELECT 1 + ? AS solution'; + + factory(function () { + var n = 0; + var trans = agent.startTransaction('foo'); + + queryablePromise.query(sql, [1]).then((rows) => { + t.strictEqual(rows[0].solution, 2); + if (++n === 3) done(); + }); + queryablePromise.query(sql, [2]).then((rows) => { + t.strictEqual(rows[0].solution, 3); + if (++n === 3) done(); + }); + queryablePromise.query(sql, [3]).then((rows) => { + t.strictEqual(rows[0].solution, 4); + if (++n === 3) done(); + }); + + function done() { + trans.end(); + } + }); + }); + + t.test('on different connections', function (t) { + resetAgent(4, function (data) { + t.strictEqual(data.transactions.length, 1); + t.strictEqual(data.spans.length, 3); + + var trans = data.transactions[0]; + + t.strictEqual(trans.name, 'foo'); + + data.spans.forEach(function (span) { + assertSpan(t, span, sql); + t.equal( + span.parent_id, + trans.id, + 'each mariadb span is a child of the transaction', + ); + }); + + t.end(); + }); + + var sql = 'SELECT 1 + ? AS solution'; + + createPool(function () { + var n = 0; + var trans = agent.startTransaction('foo'); + + queryablePromise.getConnection().then(function (conn) { + conn.query(sql, [1]).then(function (rows) { + t.strictEqual(rows[0].solution, 2); + if (++n === 3) done(); + }); + }); + queryablePromise.getConnection().then(function (conn) { + conn.query(sql, [2]).then(function (rows) { + t.strictEqual(rows[0].solution, 3); + if (++n === 3) done(); + }); + }); + queryablePromise.getConnection().then(function (conn) { + conn.query(sql, [3]).then(function (rows) { + t.strictEqual(rows[0].solution, 4); + if (++n === 3) done(); + }); + }); + + function done() { + trans.end(); + } + }); + }); + }); + + t.test('simultaneous transactions', function (t) { + resetAgent(6, function (data) { + t.strictEqual(data.transactions.length, 3); + t.strictEqual(data.spans.length, 3); + var names = data.transactions + .map(function (trans) { + return trans.name; + }) + .sort(); + t.deepEqual(names, ['bar', 'baz', 'foo']); + + data.transactions.forEach(function (trans) { + const span = findObjInArray(data.spans, 'transaction_id', trans.id); + t.ok(span, 'transaction should have span'); + assertSpan(t, span, sql); + }); + + t.end(); + }); + + var sql = 'SELECT 1 + ? AS solution'; + + factory(function () { + setImmediate(function () { + var trans = agent.startTransaction('foo'); + queryablePromise.query(sql, [1]).then(function (rows) { + t.strictEqual(rows[0].solution, 2); + trans.end(); + }); + }); + + setImmediate(function () { + var trans = agent.startTransaction('bar'); + queryablePromise.query(sql, [2]).then(function (rows) { + t.strictEqual(rows[0].solution, 3); + trans.end(); + }); + }); + + setImmediate(function () { + var trans = agent.startTransaction('baz'); + queryablePromise.query(sql, [3]).then(function (rows) { + t.strictEqual(rows[0].solution, 4); + trans.end(); + }); + }); + }); + }); + + // Only pools have a getConnection function + if (type === 'pool') { + t.test('connection.release()', function (t) { + resetAgent(function (data) { + assertBasicQuery(t, sql, data); + t.end(); + }); + + var sql = 'SELECT 1 + 1 AS solution'; + + factory(function () { + agent.startTransaction('foo'); + + queryablePromise.getConnection().then(function (conn) { + conn.release(); + + queryablePromise.getConnection().then(function (conn) { + basicQueryPromise(t, conn.query(sql)); + t.ok( + agent.currentSpan === null, + 'mariadb span should not spill into calling code', + ); + }); + }); + }); + }); + } }); }); @@ -538,6 +524,7 @@ function basicQueryCallback(t) { 'mariadb span should not spill into calling code', ); t.error(err); + console.log(rows); t.strictEqual(rows[0].solution, 2); agent.endTransaction(); }; @@ -843,7 +830,7 @@ function setup(cb) { } // placeholder variable to hold the teardown function created by the setup function -var _teardown = function () {}; +var _teardown = function () { }; var teardown = function () { _teardown(); }; From 835617f203f167b350113daec8a3339dd458c938 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eneko=20Rodr=C3=ADguez?= Date: Tue, 28 May 2024 14:26:01 +0200 Subject: [PATCH 11/13] chore: remove console log --- test/instrumentation/modules/mariadb/mariadb.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/instrumentation/modules/mariadb/mariadb.test.js b/test/instrumentation/modules/mariadb/mariadb.test.js index 2a5b088c1f9..027bf41f7ad 100644 --- a/test/instrumentation/modules/mariadb/mariadb.test.js +++ b/test/instrumentation/modules/mariadb/mariadb.test.js @@ -524,7 +524,7 @@ function basicQueryCallback(t) { 'mariadb span should not spill into calling code', ); t.error(err); - console.log(rows); + t.strictEqual(rows[0].solution, 2); agent.endTransaction(); }; From 63c3a0f82f6253eba5a888ae6941140491cea28c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eneko=20Rodr=C3=ADguez?= Date: Tue, 28 May 2024 14:38:25 +0200 Subject: [PATCH 12/13] improved supported versions to 3.1.0 --- .tav.yml | 2 +- CHANGELOG.asciidoc | 2 +- docs/supported-technologies.asciidoc | 2 +- lib/instrumentation/modules/mariadb.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.tav.yml b/.tav.yml index 13fb94ee1c3..af8c1677dfb 100644 --- a/.tav.yml +++ b/.tav.yml @@ -55,7 +55,7 @@ mysql2: - node test/instrumentation/modules/mysql2/pool-release-1.test.js mariadb: - - versions: '>=3.3.0 <4' + - versions: '>=3.1.0 <4' node: '>=14.0.0' commands: - node test/instrumentation/modules/mariadb/mariadb.test.js diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 3b2356bc89b..2df1f25897d 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -40,7 +40,7 @@ See the <> guide. [float] ===== Features -Add support for mariadb >=3.3.0 instrumentation +Add support for mariadb >=3.1.0 instrumentation [float] ===== Bug fixes diff --git a/docs/supported-technologies.asciidoc b/docs/supported-technologies.asciidoc index aa76967cf34..b4e56667c0a 100644 --- a/docs/supported-technologies.asciidoc +++ b/docs/supported-technologies.asciidoc @@ -137,7 +137,7 @@ so those should be supported as well. |https://www.npmjs.com/package/mongoose[mongoose] |>=5.7.0 <8 |Supported via mongodb |https://www.npmjs.com/package/mysql[mysql] |^2.0.0 |Will instrument all queries |https://www.npmjs.com/package/mysql2[mysql2] |>=1.0.0 <4.0.0 |Will instrument all queries -|https://www.npmjs.com/package/mariadb[mariadb] |>=3.3.0 <4.0.0 |Will instrument all queries +|https://www.npmjs.com/package/mariadb[mariadb] |>=3.1.0 <4.0.0 |Will instrument all queries |https://www.npmjs.com/package/pg[pg] |>=4.0.0 <9.0.0 |Will instrument all queries |https://www.npmjs.com/package/redis[redis] |>=2.0.0 <5.0.0 |Will instrument all queries |https://www.npmjs.com/package/tedious[tedious] |>=1.9 <19.0.0 | (Excluding v4.0.0.) Will instrument all queries diff --git a/lib/instrumentation/modules/mariadb.js b/lib/instrumentation/modules/mariadb.js index e2e9b94ef69..9dfb012398e 100644 --- a/lib/instrumentation/modules/mariadb.js +++ b/lib/instrumentation/modules/mariadb.js @@ -21,7 +21,7 @@ module.exports = function ( return mariadb; } - if (!semver.satisfies(version, '>=3.3.0')) { + if (!semver.satisfies(version, '>=3.1.0')) { agent.logger.debug( 'mariab version %s not supported - aborting...', version, From 8f391d20755825787530a429d13d66e58c98702d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eneko=20Rodr=C3=ADguez?= Date: Tue, 4 Jun 2024 16:49:51 +0200 Subject: [PATCH 13/13] Allow to use of .js The wrapper lib I use call this with the .js so don't found the instrumentation --- lib/instrumentation/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/instrumentation/index.js b/lib/instrumentation/index.js index 1fdeb12720a..c291ef2bcb2 100644 --- a/lib/instrumentation/index.js +++ b/lib/instrumentation/index.js @@ -101,6 +101,7 @@ var MODULE_PATCHERS = [ { modPath: 'mysql2' }, { modPath: 'mariadb' }, { modPath: 'mariadb/callback', patcher: './modules/mariadb.js' }, + { modPath: 'mariadb/callback.js', patcher: './modules/mariadb.js' }, { modPath: 'next' }, { modPath: 'next/dist/server/api-utils/node.js' }, { modPath: 'next/dist/server/dev/next-dev-server.js' },