From d8af64fe1e7b5b09ad5c7387f95b49844b39adce Mon Sep 17 00:00:00 2001 From: Heiko Kernbach Date: Fri, 20 Oct 2023 18:20:57 +0200 Subject: [PATCH 1/4] added new configuration file + support to execute only join relevant AQL queries --- simple/run-small-aql-joins.js | 9 +++++ simple/test.js | 67 +++++++++++++++++++++++++---------- 2 files changed, 57 insertions(+), 19 deletions(-) create mode 100644 simple/run-small-aql-joins.js diff --git a/simple/run-small-aql-joins.js b/simple/run-small-aql-joins.js new file mode 100644 index 0000000..799e414 --- /dev/null +++ b/simple/run-small-aql-joins.js @@ -0,0 +1,9 @@ +function main () { + require("./simple/test").test({ + small: true, + aqlJoinTests: true + }); +} +if (typeof arango !== "undefined") { + main(); +} diff --git a/simple/test.js b/simple/test.js index 7574a0a..2233744 100644 --- a/simple/test.js +++ b/simple/test.js @@ -1987,6 +1987,31 @@ exports.test = function (global) { ); }, + // ///////////////////////////////////////////////////////////////////////////// + // tests shared across multiple suites + // they are defined here, but need to be included manually into the specific + // suites. + // ///////////////////////////////////////////////////////////////////////////// + sharedTests = { + aqlJoinTests: [ + { + name: "aql-join-key", + params: { func: join, attr: "_key" } + }, + { + name: "aql-join-id", + params: { func: join, attr: "_id" } + }, + { + name: "aql-join-hash-number", + params: { func: join, attr: "value1" } + }, + { + name: "aql-join-hash-string", + params: { func: join, attr: "value2" } + } + ] + }, // ///////////////////////////////////////////////////////////////////////////// // main @@ -2210,22 +2235,10 @@ exports.test = function (global) { name: "aql-extract-string-nonindexed", params: { func: extract, attr: "value6" } }, - { - name: "aql-join-key", - params: { func: join, attr: "_key" } - }, - { - name: "aql-join-id", - params: { func: join, attr: "_id" } - }, - { - name: "aql-join-hash-number", - params: { func: join, attr: "value1" } - }, - { - name: "aql-join-hash-string", - params: { func: join, attr: "value2" } - }, + sharedTests.aqlJoinTests[0], + sharedTests.aqlJoinTests[1], + sharedTests.aqlJoinTests[2], + sharedTests.aqlJoinTests[3], { name: "aql-lookup-key", params: { func: lookup, attr: "_key", n: 10000, numeric: false } @@ -2275,6 +2288,12 @@ exports.test = function (global) { params: { func: rangesSubquery, optimize: false, distinct: true } }, ], + aqlJoinTests = [ + sharedTests.aqlJoinTests[0], + sharedTests.aqlJoinTests[1], + sharedTests.aqlJoinTests[2], + sharedTests.aqlJoinTests[3], + ], // Tests without collections/IO, to focus on aql block performance. iolessTests = [ { @@ -3032,7 +3051,7 @@ exports.test = function (global) { const runSatelliteGraphTests = (global.satelliteGraphTests && isEnterprise && isCluster); - if (global.documents || global.edges || global.noMaterializationSearch || global.subqueryTests || runSatelliteGraphTests ) { + if (global.documents || global.edges || global.noMaterializationSearch || global.subqueryTests || runSatelliteGraphTests || global.aqlJoinTests) { initializeValuesCollection(); } if (global.search) { @@ -3076,8 +3095,7 @@ exports.test = function (global) { } } - // document tests - if (global.documents) { + const generateDocumentTestOptions = () => { options = { runs: global.runs, digits: global.digits, @@ -3099,9 +3117,20 @@ exports.test = function (global) { options.collections.push({ name: "values1000000", label: "1000k", size: 1000000 }); } + return options; + }; + + // document tests + if (global.documents) { + const options = generateDocumentTestOptions(); runTestSuite("Documents", documentTests, options); } + if (global.aqlJoinTests) { + const options = generateDocumentTestOptions(); + runTestSuite("AqlJoin", aqlJoinTests, options); + } + if (global.ioless) { options = { runs: global.runs, From 89c81e0bfe682182a55455bbdd6efc5a494a4efa Mon Sep 17 00:00:00 2001 From: Heiko Kernbach Date: Fri, 20 Oct 2023 18:25:06 +0200 Subject: [PATCH 2/4] also added medium and big configuration for aql joins --- simple/run-big-aql-joins.js | 9 +++++++++ simple/run-medium-aql-joins.js | 9 +++++++++ 2 files changed, 18 insertions(+) create mode 100644 simple/run-big-aql-joins.js create mode 100644 simple/run-medium-aql-joins.js diff --git a/simple/run-big-aql-joins.js b/simple/run-big-aql-joins.js new file mode 100644 index 0000000..e197044 --- /dev/null +++ b/simple/run-big-aql-joins.js @@ -0,0 +1,9 @@ +function main () { + require("./simple/test").test({ + big: true, + aqlJoinTests: true + }); +} +if (typeof arango !== "undefined") { + main(); +} diff --git a/simple/run-medium-aql-joins.js b/simple/run-medium-aql-joins.js new file mode 100644 index 0000000..68f9d30 --- /dev/null +++ b/simple/run-medium-aql-joins.js @@ -0,0 +1,9 @@ +function main () { + require("./simple/test").test({ + medium: true, + aqlJoinTests: true + }); +} +if (typeof arango !== "undefined") { + main(); +} From 899a4438ed6e9d36e65a00a96a0560556fcc8bb2 Mon Sep 17 00:00:00 2001 From: Heiko Kernbach Date: Fri, 20 Oct 2023 18:39:23 +0200 Subject: [PATCH 3/4] modified suite: aqlJoinTests to be able to run each defined tests with different optimizer rules set --- simple/test.js | 51 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/simple/test.js b/simple/test.js index 2233744..28d6a5d 100644 --- a/simple/test.js +++ b/simple/test.js @@ -1436,6 +1436,23 @@ exports.test = function (global) { ); }, + joinWithoutJoinNodeOptimizerRule = function (params) { + let queryOptions = { + silent: silent, + rules: ["-join-index-nodes"] + }; + + db._query( + "FOR c1 IN @@c FOR c2 IN @@c FILTER c1.@attr == c2.@attr RETURN c1", + { + "@c": params.collection, + attr: params.attr + }, + {}, + queryOptions + ); + }, + lookup = function (params) { let key, numeric = params.numeric; @@ -2009,6 +2026,22 @@ exports.test = function (global) { { name: "aql-join-hash-string", params: { func: join, attr: "value2" } + }, + { + name: "aql-join-key-disabled-index-join-node", + params: { func: joinWithoutJoinNodeOptimizerRule, attr: "_key" } + }, + { + name: "aql-join-id-disabled-index-join-node", + params: { func: joinWithoutJoinNodeOptimizerRule, attr: "_id" } + }, + { + name: "aql-join-hash-number-disabled-index-join-node", + params: { func: joinWithoutJoinNodeOptimizerRule, attr: "value1" } + }, + { + name: "aql-join-hash-string-disabled-index-join-node", + params: { func: joinWithoutJoinNodeOptimizerRule, attr: "value2" } } ] }, @@ -2289,10 +2322,20 @@ exports.test = function (global) { }, ], aqlJoinTests = [ - sharedTests.aqlJoinTests[0], - sharedTests.aqlJoinTests[1], - sharedTests.aqlJoinTests[2], - sharedTests.aqlJoinTests[3], + // line breaks intended. Each pair belongs together. Same test executed + // with different optimizer based settings. Code itself could be optimized, + // but for our use-case right now it is fine enough. + sharedTests.aqlJoinTests[0], // default, means: no optimizer manipulation + sharedTests.aqlJoinTests[4], // disabled optimizer rule + + sharedTests.aqlJoinTests[1], // default, means: no optimizer manipulation + sharedTests.aqlJoinTests[5], // disabled optimizer rule + + sharedTests.aqlJoinTests[2], // default, means: no optimizer manipulation + sharedTests.aqlJoinTests[6], // disabled optimizer rule + + sharedTests.aqlJoinTests[3], // default, means: no optimizer manipulation + sharedTests.aqlJoinTests[7], // disabled optimizer rule ], // Tests without collections/IO, to focus on aql block performance. iolessTests = [ From e9928ef9b608a9cc3e2d87fa8d185c96d71d231f Mon Sep 17 00:00:00 2001 From: Heiko Kernbach Date: Fri, 20 Oct 2023 19:53:09 +0200 Subject: [PATCH 4/4] added some debug logging mechanism to the newly introduced aqlJoinTests suite. This is off by default and can be turned on by setting global.printQueryCount to true --- simple/run-big-aql-joins.js | 3 +- simple/run-medium-aql-joins.js | 3 +- simple/run-small-aql-joins.js | 3 +- simple/test.js | 90 ++++++++++++++++++++++++++-------- 4 files changed, 75 insertions(+), 24 deletions(-) diff --git a/simple/run-big-aql-joins.js b/simple/run-big-aql-joins.js index e197044..bb4fbc6 100644 --- a/simple/run-big-aql-joins.js +++ b/simple/run-big-aql-joins.js @@ -1,7 +1,8 @@ function main () { require("./simple/test").test({ big: true, - aqlJoinTests: true + aqlJoinTests: true, + printQueryCount: false }); } if (typeof arango !== "undefined") { diff --git a/simple/run-medium-aql-joins.js b/simple/run-medium-aql-joins.js index 68f9d30..e4ddd51 100644 --- a/simple/run-medium-aql-joins.js +++ b/simple/run-medium-aql-joins.js @@ -1,7 +1,8 @@ function main () { require("./simple/test").test({ medium: true, - aqlJoinTests: true + aqlJoinTests: true, + printQueryCount: false }); } if (typeof arango !== "undefined") { diff --git a/simple/run-small-aql-joins.js b/simple/run-small-aql-joins.js index 799e414..3ebb56d 100644 --- a/simple/run-small-aql-joins.js +++ b/simple/run-small-aql-joins.js @@ -1,7 +1,8 @@ function main () { require("./simple/test").test({ small: true, - aqlJoinTests: true + aqlJoinTests: true, + printQueryCount: false }); } if (typeof arango !== "undefined") { diff --git a/simple/test.js b/simple/test.js index 28d6a5d..9890d55 100644 --- a/simple/test.js +++ b/simple/test.js @@ -1425,32 +1425,76 @@ exports.test = function (global) { }, join = function (params) { - db._query( - "FOR c1 IN @@c FOR c2 IN @@c FILTER c1.@attr == c2.@attr RETURN c1", - { - "@c": params.collection, - attr: params.attr - }, - {}, - { silent } + let queryOptions = { + silent: silent + }; + + if (global.printQueryCount) { + queryOptions.count = true; + } + + const queryString = "FOR c1 IN @@c FOR c2 IN @@c FILTER c1.@attr == c2.@attr RETURN c1"; + const bindParameter = { + "@c": params.collection, + attr: params.attr + }; + + const q = db._query( + queryString, + bindParameter, + {}, // cursorOptions + queryOptions ); + + if (global.printQueryCount) { + print("========================================"); + print("Default execution - no optimizer impact.") + const plan = db._createStatement({ + query: queryString, bindVars: bindParameter, options: queryOptions + }).explain().plan; const nodes = plan.nodes.map(x => x.type); + const found = nodes.indexOf("JoinNode") !== -1; + + print(nodes); + print("Count is: " + q.count() + ", used JoinNode: " + found); + } }, joinWithoutJoinNodeOptimizerRule = function (params) { let queryOptions = { silent: silent, - rules: ["-join-index-nodes"] + optimizer: { + rules: ["-join-index-nodes"] + } }; - db._query( - "FOR c1 IN @@c FOR c2 IN @@c FILTER c1.@attr == c2.@attr RETURN c1", - { - "@c": params.collection, - attr: params.attr - }, - {}, + if (global.printQueryCount) { + queryOptions.count = true; + } + + const queryString = "FOR c1 IN @@c FOR c2 IN @@c FILTER c1.@attr == c2.@attr RETURN c1"; + const bindParameter = { + "@c": params.collection, + attr: params.attr + }; + + const q = db._query( + queryString, + bindParameter, + {}, // cursorOptions queryOptions ); + + if (global.printQueryCount) { + print("========================================"); + print("Disabled rule execution - with optimizer impact.") + const plan = db._createStatement({ + query: queryString, bindVars: bindParameter, options: queryOptions + }).explain().plan; + const nodes = plan.nodes.map(x => x.type); + const found = nodes.indexOf("JoinNode") !== -1; + print(nodes); + print("Count is: " + q.count() + ", used JoinNode: " + found); + } }, lookup = function (params) { @@ -2325,15 +2369,17 @@ exports.test = function (global) { // line breaks intended. Each pair belongs together. Same test executed // with different optimizer based settings. Code itself could be optimized, // but for our use-case right now it is fine enough. - sharedTests.aqlJoinTests[0], // default, means: no optimizer manipulation - sharedTests.aqlJoinTests[4], // disabled optimizer rule + //sharedTests.aqlJoinTests[0], // default, means: no optimizer manipulation + //sharedTests.aqlJoinTests[4], // disabled optimizer rule - sharedTests.aqlJoinTests[1], // default, means: no optimizer manipulation - sharedTests.aqlJoinTests[5], // disabled optimizer rule + //sharedTests.aqlJoinTests[1], // default, means: no optimizer manipulation + //sharedTests.aqlJoinTests[5], // disabled optimizer rule + // join-hash-number-string sharedTests.aqlJoinTests[2], // default, means: no optimizer manipulation sharedTests.aqlJoinTests[6], // disabled optimizer rule + // join-hash-string sharedTests.aqlJoinTests[3], // default, means: no optimizer manipulation sharedTests.aqlJoinTests[7], // disabled optimizer rule ], @@ -3171,7 +3217,9 @@ exports.test = function (global) { if (global.aqlJoinTests) { const options = generateDocumentTestOptions(); - runTestSuite("AqlJoin", aqlJoinTests, options); + // Currently, only hashed variants are being executed here. + // The other ones are commented out right now in "aqlJoinTests". + runTestSuite("AqlJoinHashOnly", aqlJoinTests, options); } if (global.ioless) {