From 784cdf4758b035128d92290a1ee1b707e661a981 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Sep 2023 15:38:40 +0000 Subject: [PATCH 01/10] Bump actions/checkout from 3 to 4 in /.github/workflows (#352) Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/maven.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index d4d1b7d90..62f24d4e7 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -41,7 +41,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up JDK uses: actions/setup-java@v3 @@ -61,7 +61,7 @@ jobs: profile: [ 'cockroachdb', 'mariadb', 'mysql', 'postgres', 'spanner', 'phoenix', 'sqlserver', 'sqlite' ] steps: - name: Checkout repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up JDK uses: actions/setup-java@v3 @@ -459,7 +459,7 @@ jobs: PGPASSWORD=password dropdb -h localhost -U admin benchbase --if-exists PGPASSWORD=password createdb -h localhost -U admin benchbase - name: Checkout repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 # https://github.com/actions/cache/blob/master/examples.md#java---maven - name: Cache local Maven repository uses: actions/cache@v3 From 0119278bba62b06169e4a95974bdd61504814dae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Sep 2023 16:45:02 +0000 Subject: [PATCH 02/10] Bump maven-dependency-plugin from 3.5.0 to 3.6.0 (#319) Bumps [maven-dependency-plugin](https://github.com/apache/maven-dependency-plugin) from 3.5.0 to 3.6.0. - [Commits](https://github.com/apache/maven-dependency-plugin/compare/maven-dependency-plugin-3.5.0...maven-dependency-plugin-3.6.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-dependency-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7ea48340e..c6671bf0c 100644 --- a/pom.xml +++ b/pom.xml @@ -343,7 +343,7 @@ org.apache.maven.plugins maven-dependency-plugin - 3.5.0 + 3.6.0 analyze From 109064ef368e73a12ae1db33c64ef90af44f8aef Mon Sep 17 00:00:00 2001 From: nuno-faria Date: Tue, 26 Sep 2023 12:34:48 +0100 Subject: [PATCH 03/10] Fix concurrent modification error in AuctionMark (#357) Co-authored-by: Andy Pavlo --- .../auctionmark/AuctionMarkProfile.java | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/oltpbenchmark/benchmarks/auctionmark/AuctionMarkProfile.java b/src/main/java/com/oltpbenchmark/benchmarks/auctionmark/AuctionMarkProfile.java index 214d576d4..fb3a38f00 100644 --- a/src/main/java/com/oltpbenchmark/benchmarks/auctionmark/AuctionMarkProfile.java +++ b/src/main/java/com/oltpbenchmark/benchmarks/auctionmark/AuctionMarkProfile.java @@ -746,8 +746,8 @@ public void updateItemQueues() { continue; } - for (ItemInfo itemInfo : items) { - this.addItemToProperQueue(itemInfo, currentTime); + for (Iterator it = items.iterator(); it.hasNext();) { + this.addItemToProperQueue(it.next(), currentTime, it); } } @@ -767,10 +767,10 @@ public ItemStatus addItemToProperQueue(ItemInfo itemInfo, boolean is_loader) { Timestamp baseTime = (is_loader ? this.getLoaderStartTime() : this.getCurrentTime()); - return addItemToProperQueue(itemInfo, baseTime); + return addItemToProperQueue(itemInfo, baseTime, null); } - private ItemStatus addItemToProperQueue(ItemInfo itemInfo, Timestamp baseTime) { + private ItemStatus addItemToProperQueue(ItemInfo itemInfo, Timestamp baseTime, Iterator currentQueueIterator) { // Always check whether we even want it for this client // The loader's profile and the cache profile will always have a negative client_id, // which means that we always want to keep it @@ -799,30 +799,25 @@ private ItemStatus addItemToProperQueue(ItemInfo itemInfo, Timestamp baseTime) { if (!new_status.equals(existingStatus)) { + // Remove the item from the current queue + if (currentQueueIterator != null) { + currentQueueIterator.remove(); + } + switch (new_status) { case OPEN: this.addItem(this.items_available, itemInfo); break; case ENDING_SOON: - this.items_available.remove(itemInfo); this.addItem(this.items_endingSoon, itemInfo); break; case WAITING_FOR_PURCHASE: - (existingStatus == ItemStatus.OPEN ? this.items_available : this.items_endingSoon).remove(itemInfo); this.addItem(this.items_waitingForPurchase, itemInfo); break; case CLOSED: - if (existingStatus == ItemStatus.OPEN) { - this.items_available.remove(itemInfo); - } else if (existingStatus == ItemStatus.ENDING_SOON) { - this.items_endingSoon.remove(itemInfo); - } else { - this.items_waitingForPurchase.remove(itemInfo); - } this.addItem(this.items_completed, itemInfo); break; default: - } itemInfo.setStatus(new_status); } From 611f3d49e88c99a76803e546a43b054e327b8ec4 Mon Sep 17 00:00:00 2001 From: anjagruenheid <87397397+anjagruenheid@users.noreply.github.com> Date: Mon, 2 Oct 2023 23:36:57 +0200 Subject: [PATCH 04/10] Introduce templated benchmarks. (#301) This PR introduces templated benchmarks for BenchBase. In essence, users can define their own query templates that are parametrized with one or more values at runtime. If multiple parameter combinations are possible, we will continue to cycle through all available combinations. A templated benchmark has the following structure: ```xml ... ``` where `$ParameterType` is the integer `java.sql.Types` value (i.e., `INTEGER`, `VARCHAR`, etc.) and each value tag within `values` contains the values for one instantiation of the parameters set in `$SQLQuery`. The SQL query string is read as a `PreparedStatement`, i.e., parameters are defined in the string via a `?` placeholder. An example for a templated benchmark can be found in `data/templated/example.xml`. The file path for the XML template has to be defined in the workload configuration using the `query_templates_file` tag. An example configuration can be found in `config/sqlserver/sample_template_config.xml`. The example can be executed if a loaded TPC-C instance is used as JDBC endpoint. Templated benchmarks are instantiated using `templated` as benchmark class when running BenchBase via the command line. --- .github/workflows/maven.yml | 64 +++- config/mariadb/sample_templated_config.xml | 49 +++ config/mysql/sample_templated_config.xml | 49 +++ config/plugin.xml | 1 + config/postgres/sample_templated_config.xml | 49 +++ config/sqlite/sample_templated_config.xml | 47 +++ config/sqlite/sample_tpcc_config.xml | 2 +- config/sqlite/sample_tpcc_nosync_config.xml | 55 ++++ config/sqlserver/sample_templated_config.xml | 49 +++ data/templated/example.xml | 64 ++++ docker/build-run-benchmark-with-docker.sh | 24 ++ pom.xml | 13 + .../java/com/oltpbenchmark/DBWorkload.java | 4 +- .../oltpbenchmark/api/BenchmarkModule.java | 16 +- .../api/templates/ObjectFactory.java | 88 ++++++ .../api/templates/TemplateType.java | 102 +++++++ .../api/templates/TemplatesType.java | 74 +++++ .../api/templates/TypesType.java | 70 +++++ .../api/templates/ValuesType.java | 70 +++++ .../benchmarks/templated/README.md | 56 ++++ .../templated/TemplatedBenchmark.java | 280 ++++++++++++++++++ .../benchmarks/templated/TemplatedWorker.java | 66 +++++ .../templated/procedures/GenericQuery.java | 99 +++++++ .../templated/util/GenericQueryOperation.java | 41 +++ .../util/TraceTransactionGenerator.java | 47 +++ .../distributions/CyclicCounterGenerator.java | 63 ++++ .../com/oltpbenchmark/util/ClassUtil.java | 18 +- .../java/com/oltpbenchmark/util/SQLUtil.java | 1 - src/main/resources/templates.xsd | 53 ++++ .../oltpbenchmark/api/AbstractTestCase.java | 67 +++-- .../oltpbenchmark/api/AbstractTestWorker.java | 5 +- .../benchmarks/templated/README.md | 9 + .../templated/TestTemplatedWorker.java | 123 ++++++++ 33 files changed, 1775 insertions(+), 43 deletions(-) create mode 100644 config/mariadb/sample_templated_config.xml create mode 100644 config/mysql/sample_templated_config.xml create mode 100644 config/postgres/sample_templated_config.xml create mode 100644 config/sqlite/sample_templated_config.xml create mode 100644 config/sqlite/sample_tpcc_nosync_config.xml create mode 100644 config/sqlserver/sample_templated_config.xml create mode 100644 data/templated/example.xml create mode 100644 src/main/java/com/oltpbenchmark/api/templates/ObjectFactory.java create mode 100644 src/main/java/com/oltpbenchmark/api/templates/TemplateType.java create mode 100644 src/main/java/com/oltpbenchmark/api/templates/TemplatesType.java create mode 100644 src/main/java/com/oltpbenchmark/api/templates/TypesType.java create mode 100644 src/main/java/com/oltpbenchmark/api/templates/ValuesType.java create mode 100644 src/main/java/com/oltpbenchmark/benchmarks/templated/README.md create mode 100644 src/main/java/com/oltpbenchmark/benchmarks/templated/TemplatedBenchmark.java create mode 100644 src/main/java/com/oltpbenchmark/benchmarks/templated/TemplatedWorker.java create mode 100644 src/main/java/com/oltpbenchmark/benchmarks/templated/procedures/GenericQuery.java create mode 100644 src/main/java/com/oltpbenchmark/benchmarks/templated/util/GenericQueryOperation.java create mode 100644 src/main/java/com/oltpbenchmark/benchmarks/templated/util/TraceTransactionGenerator.java create mode 100644 src/main/java/com/oltpbenchmark/distributions/CyclicCounterGenerator.java create mode 100644 src/main/resources/templates.xsd create mode 100644 src/test/java/com/oltpbenchmark/benchmarks/templated/README.md create mode 100644 src/test/java/com/oltpbenchmark/benchmarks/templated/TestTemplatedWorker.java diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 62f24d4e7..4036e97bf 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -89,7 +89,7 @@ jobs: fail-fast: false matrix: # BROKEN: tpch - benchmark: [ 'epinions', 'hyadapt', 'noop', 'otmetrics', 'resourcestresser', 'seats', 'sibench', 'smallbank', 'tatp', 'tpcc', 'twitter', 'voter', 'wikipedia', 'ycsb' ] + benchmark: [ 'epinions', 'hyadapt', 'noop', 'otmetrics', 'resourcestresser', 'seats', 'sibench', 'smallbank', 'tatp', 'templated', 'tpcc', 'twitter', 'voter', 'wikipedia', 'ycsb' ] steps: - name: Download artifact uses: actions/download-artifact@v3 @@ -112,7 +112,16 @@ jobs: - name: Run benchmark run: | - java -jar benchbase.jar -b ${{matrix.benchmark}} -c config/sqlite/sample_${{matrix.benchmark}}_config.xml --create=true --load=true --execute=true --json-histograms results/histograms.json + # For templated benchmarks, we need to preload some data for the test since by design, templated benchmarks do not support the 'load' operation + # In this case, we load the tpcc data. + if [[ ${{matrix.benchmark}} == templated ]]; then + # Disable synchronous mode for sqlite tpcc data loading to save some time. + java -jar benchbase.jar -b tpcc -c config/sqlite/sample_tpcc_nosync_config.xml --create=true --load=true --execute=false --json-histograms results/histograms.json + # Run the templated benchmark. + java -jar benchbase.jar -b ${{matrix.benchmark}} -c config/sqlite/sample_${{matrix.benchmark}}_config.xml --create=false --load=false --execute=true --json-histograms results/histograms.json + else + java -jar benchbase.jar -b ${{matrix.benchmark}} -c config/sqlite/sample_${{matrix.benchmark}}_config.xml --create=true --load=true --execute=true --json-histograms results/histograms.json + fi # FIXME: Reduce the error rate so we don't need these overrides. if [ ${{matrix.benchmark}} == auctionmark ]; then ERRORS_THRESHOLD=0.02 @@ -134,7 +143,7 @@ jobs: strategy: fail-fast: false matrix: - benchmark: [ 'auctionmark', 'epinions', 'hyadapt', 'noop', 'otmetrics', 'resourcestresser', 'seats', 'sibench', 'smallbank', 'tatp', 'tpcc', 'twitter', 'voter', 'wikipedia', 'ycsb' ] + benchmark: [ 'auctionmark', 'epinions', 'hyadapt', 'noop', 'otmetrics', 'resourcestresser', 'seats', 'sibench', 'smallbank', 'tatp', 'templated', 'tpcc', 'twitter', 'voter', 'wikipedia', 'ycsb' ] services: mariadb: # https://hub.docker.com/_/mariadb image: mariadb:latest @@ -176,7 +185,16 @@ jobs: MARIADB_PORT: ${{ job.services.mariadb.ports[3306] }} run: | mysql -h127.0.0.1 -P$MARIADB_PORT -uadmin -ppassword -e "DROP DATABASE IF EXISTS benchbase; CREATE DATABASE benchbase" - java -jar benchbase.jar -b ${{matrix.benchmark}} -c config/mariadb/sample_${{matrix.benchmark}}_config.xml --create=true --load=true --execute=true --json-histograms results/histograms.json + + # For templated benchmarks, we need to preload some data for the test since by design, templated benchmarks do not support the 'load' operation + # In this case, we load the tpcc data. + if [[ ${{matrix.benchmark}} == templated ]]; then + java -jar benchbase.jar -b tpcc -c config/mariadb/sample_tpcc_config.xml --create=true --load=true --execute=false --json-histograms results/histograms.json + java -jar benchbase.jar -b ${{matrix.benchmark}} -c config/mariadb/sample_${{matrix.benchmark}}_config.xml --create=false --load=false --execute=true --json-histograms results/histograms.json + else + java -jar benchbase.jar -b ${{matrix.benchmark}} -c config/mariadb/sample_${{matrix.benchmark}}_config.xml --create=true --load=true --execute=true --json-histograms results/histograms.json + fi + # FIXME: Reduce the error rate so we don't need these overrides. if [ ${{matrix.benchmark}} == auctionmark ]; then ERRORS_THRESHOLD=0.02 @@ -194,7 +212,7 @@ jobs: strategy: fail-fast: false matrix: - benchmark: [ 'auctionmark', 'epinions', 'hyadapt', 'noop', 'otmetrics', 'resourcestresser', 'seats', 'sibench', 'smallbank', 'tatp', 'tpcc', 'twitter', 'voter', 'wikipedia', 'ycsb' ] + benchmark: [ 'auctionmark', 'epinions', 'hyadapt', 'noop', 'otmetrics', 'resourcestresser', 'seats', 'sibench', 'smallbank', 'tatp', 'templated', 'tpcc', 'twitter', 'voter', 'wikipedia', 'ycsb' ] services: mysql: # https://hub.docker.com/_/mysql image: mysql:latest @@ -235,7 +253,16 @@ jobs: MYSQL_PORT: ${{ job.services.mysql.ports[3306] }} run: | mysql -h127.0.0.1 -P$MYSQL_PORT -uadmin -ppassword -e "DROP DATABASE IF EXISTS benchbase; CREATE DATABASE benchbase" - java -jar benchbase.jar -b ${{matrix.benchmark}} -c config/mysql/sample_${{matrix.benchmark}}_config.xml --create=true --load=true --execute=true --json-histograms results/histograms.json + + # For templated benchmarks, we need to preload some data for the test since by design, templated benchmarks do not support the 'load' operation + # In this case, we load the tpcc data. + if [[ ${{matrix.benchmark}} == templated ]]; then + java -jar benchbase.jar -b tpcc -c config/mysql/sample_tpcc_config.xml --create=true --load=true --execute=false --json-histograms results/histograms.json + java -jar benchbase.jar -b ${{matrix.benchmark}} -c config/mysql/sample_${{matrix.benchmark}}_config.xml --create=false --load=false --execute=true --json-histograms results/histograms.json + else + java -jar benchbase.jar -b ${{matrix.benchmark}} -c config/mysql/sample_${{matrix.benchmark}}_config.xml --create=true --load=true --execute=true --json-histograms results/histograms.json + fi + # FIXME: Reduce the error rate so we don't need these overrides. if [ ${{matrix.benchmark}} == auctionmark ]; then ERRORS_THRESHOLD=0.02 @@ -253,7 +280,7 @@ jobs: strategy: fail-fast: false matrix: - benchmark: [ 'auctionmark', 'epinions', 'hyadapt', 'noop', 'otmetrics', 'resourcestresser', 'seats', 'sibench', 'smallbank', 'tatp', 'tpcc', 'tpch', 'twitter', 'voter', 'wikipedia', 'ycsb' ] + benchmark: [ 'auctionmark', 'epinions', 'hyadapt', 'noop', 'otmetrics', 'resourcestresser', 'seats', 'sibench', 'smallbank', 'tatp', 'templated', 'tpcc', 'tpch', 'twitter', 'voter', 'wikipedia', 'ycsb' ] services: postgres: # https://hub.docker.com/_/postgres image: postgres:latest @@ -292,7 +319,16 @@ jobs: run: | PGPASSWORD=password dropdb -h localhost -U admin benchbase --if-exists PGPASSWORD=password createdb -h localhost -U admin benchbase - java -jar benchbase.jar -b ${{matrix.benchmark}} -c config/postgres/sample_${{matrix.benchmark}}_config.xml --create=true --load=true --execute=true --json-histograms results/histograms.json + + # For templated benchmarks, we need to preload some data for the test since by design, templated benchmarks do not support the 'load' operation + # In this case, we load the tpcc data. + if [[ ${{matrix.benchmark}} == templated ]]; then + java -jar benchbase.jar -b tpcc -c config/postgres/sample_tpcc_config.xml --create=true --load=true --execute=false --json-histograms results/histograms.json + java -jar benchbase.jar -b ${{matrix.benchmark}} -c config/postgres/sample_${{matrix.benchmark}}_config.xml --create=false --load=false --execute=true --json-histograms results/histograms.json + else + java -jar benchbase.jar -b ${{matrix.benchmark}} -c config/postgres/sample_${{matrix.benchmark}}_config.xml --create=true --load=true --execute=true --json-histograms results/histograms.json + fi + # FIXME: Reduce the error rate so we don't need these overrides. if [ ${{matrix.benchmark}} == auctionmark ]; then ERRORS_THRESHOLD=0.02 @@ -362,7 +398,7 @@ jobs: matrix: # TODO: add more benchmarks #benchmark: [ 'auctionmark', 'epinions', 'hyadapt', 'noop', 'otmetrics', 'resourcestresser', 'seats', 'sibench', 'smallbank', 'tatp', 'tpcc', 'tpch', 'twitter', 'voter', 'wikipedia', 'ycsb' ] - benchmark: [ 'epinions', 'hyadapt', 'noop', 'otmetrics', 'resourcestresser', 'sibench', 'smallbank', 'tatp', 'tpcc', 'tpch', 'twitter', 'voter', 'wikipedia', 'ycsb' ] + benchmark: [ 'epinions', 'hyadapt', 'noop', 'otmetrics', 'resourcestresser', 'sibench', 'smallbank', 'tatp', 'tpcc', 'templated', 'tpch', 'twitter', 'voter', 'wikipedia', 'ycsb' ] services: sqlserver: image: mcr.microsoft.com/mssql/server:latest @@ -423,7 +459,15 @@ jobs: - name: Run benchmark # Note: user/pass should match those used in sample configs. run: | - java -jar benchbase.jar -b ${{matrix.benchmark}} -c config/sqlserver/sample_${{matrix.benchmark}}_config.xml --create=true --load=true --execute=true --json-histograms results/histograms.json + # For templated benchmarks, we need to preload some data for the test since by design, templated benchmarks do not support the 'load' operation + # In this case, we load the tpcc data. + if [[ ${{matrix.benchmark}} == templated ]]; then + java -jar benchbase.jar -b tpcc -c config/sqlserver/sample_tpcc_config.xml --create=true --load=true --execute=false --json-histograms results/histograms.json + java -jar benchbase.jar -b ${{matrix.benchmark}} -c config/sqlserver/sample_${{matrix.benchmark}}_config.xml --create=false --load=false --execute=true --json-histograms results/histograms.json + else + java -jar benchbase.jar -b ${{matrix.benchmark}} -c config/sqlserver/sample_${{matrix.benchmark}}_config.xml --create=true --load=true --execute=true --json-histograms results/histograms.json + fi + # FIXME: Reduce the error rate so we don't need these overrides. if [ ${{matrix.benchmark}} == tatp ]; then ERRORS_THRESHOLD=0.05 diff --git a/config/mariadb/sample_templated_config.xml b/config/mariadb/sample_templated_config.xml new file mode 100644 index 000000000..506469f45 --- /dev/null +++ b/config/mariadb/sample_templated_config.xml @@ -0,0 +1,49 @@ + + + + + MARIADB + org.mariadb.jdbc.Driver + jdbc:mariadb://localhost:3306/benchbase?useServerPrepStmts + admin + password + TRANSACTION_SERIALIZABLE + 128 + + + + data/templated/example.xml + + + 1 + + + + 100 + 30,20,10,30,10 + + + + + + + GetOrder + + + GetCust + + + GetCustNull + + + GetWarehouse + + + GetItemByPrice + + + diff --git a/config/mysql/sample_templated_config.xml b/config/mysql/sample_templated_config.xml new file mode 100644 index 000000000..ef10edc33 --- /dev/null +++ b/config/mysql/sample_templated_config.xml @@ -0,0 +1,49 @@ + + + + + MYSQL + com.mysql.cj.jdbc.Driver + jdbc:mysql://localhost:3306/benchbase?rewriteBatchedStatements=true&allowPublicKeyRetrieval=True&sslMode=DISABLED + admin + password + TRANSACTION_SERIALIZABLE + 128 + + + + data/templated/example.xml + + + 1 + + + + 100 + 30,20,10,30,10 + + + + + + + GetOrder + + + GetCust + + + GetCustNull + + + GetWarehouse + + + GetItemByPrice + + + diff --git a/config/plugin.xml b/config/plugin.xml index a31108273..baf699147 100644 --- a/config/plugin.xml +++ b/config/plugin.xml @@ -17,4 +17,5 @@ com.oltpbenchmark.benchmarks.smallbank.SmallBankBenchmark com.oltpbenchmark.benchmarks.hyadapt.HYADAPTBenchmark com.oltpbenchmark.benchmarks.otmetrics.OTMetricsBenchmark + com.oltpbenchmark.benchmarks.templated.TemplatedBenchmark diff --git a/config/postgres/sample_templated_config.xml b/config/postgres/sample_templated_config.xml new file mode 100644 index 000000000..2ef942e93 --- /dev/null +++ b/config/postgres/sample_templated_config.xml @@ -0,0 +1,49 @@ + + + + + POSTGRES + org.postgresql.Driver + jdbc:postgresql://localhost:5432/benchbase?sslmode=disable&ApplicationName=templated&reWriteBatchedInserts=true + admin + password + TRANSACTION_SERIALIZABLE + 128 + + + + data/templated/example.xml + + + 1 + + + + 100 + 30,20,10,30,10 + + + + + + + GetOrder + + + GetCust + + + GetCustNull + + + GetWarehouse + + + GetItemByPrice + + + diff --git a/config/sqlite/sample_templated_config.xml b/config/sqlite/sample_templated_config.xml new file mode 100644 index 000000000..e8d5a0835 --- /dev/null +++ b/config/sqlite/sample_templated_config.xml @@ -0,0 +1,47 @@ + + + + + SQLITE + org.sqlite.JDBC + jdbc:sqlite:tpcc.db + TRANSACTION_SERIALIZABLE + 128 + + + + data/templated/example.xml + + + 1 + + + + 100 + 30,20,10,30,10 + + + + + + + GetOrder + + + GetCust + + + GetCustNull + + + GetWarehouse + + + GetItemByPrice + + + diff --git a/config/sqlite/sample_tpcc_config.xml b/config/sqlite/sample_tpcc_config.xml index c54688835..3b9901886 100644 --- a/config/sqlite/sample_tpcc_config.xml +++ b/config/sqlite/sample_tpcc_config.xml @@ -10,7 +10,7 @@ 1 - + 1 diff --git a/config/sqlite/sample_tpcc_nosync_config.xml b/config/sqlite/sample_tpcc_nosync_config.xml new file mode 100644 index 000000000..bea1e6e87 --- /dev/null +++ b/config/sqlite/sample_tpcc_nosync_config.xml @@ -0,0 +1,55 @@ + + + + + SQLITE + org.sqlite.JDBC + jdbc:sqlite:tpcc.db?synchronous=off + TRANSACTION_SERIALIZABLE + 128 + + + 1 + + + 1 + + + 1 + + + + 10000 + 45,43,4,4,4 + + + + + + + NewOrder + + + + + Payment + + + + + OrderStatus + + + + + Delivery + + + + + StockLevel + + + + + diff --git a/config/sqlserver/sample_templated_config.xml b/config/sqlserver/sample_templated_config.xml new file mode 100644 index 000000000..148fb9a9f --- /dev/null +++ b/config/sqlserver/sample_templated_config.xml @@ -0,0 +1,49 @@ + + + + + sqlserver + com.microsoft.sqlserver.jdbc.SQLServerDriver + jdbc:sqlserver://localhost:1433;encrypt=false;database=benchbase; + benchuser01 + P@ssw0rd + TRANSACTION_SERIALIZABLE + 128 + + + + data/templated/example.xml + + + 1 + + + + 100 + 30,20,10,30,10 + + + + + + + GetOrder + + + GetCust + + + GetCustNull + + + GetWarehouse + + + GetItemByPrice + + + diff --git a/data/templated/example.xml b/data/templated/example.xml new file mode 100644 index 000000000..2e2177859 --- /dev/null +++ b/data/templated/example.xml @@ -0,0 +1,64 @@ + + + + + + + + + + \ No newline at end of file diff --git a/docker/build-run-benchmark-with-docker.sh b/docker/build-run-benchmark-with-docker.sh index a897bf3d9..3643da0c2 100755 --- a/docker/build-run-benchmark-with-docker.sh +++ b/docker/build-run-benchmark-with-docker.sh @@ -27,6 +27,11 @@ if [ "$BENCHBASE_PROFILE" == 'sqlite' ]; then SRC_DIR="$LOCAL_WORKSPACE_FOLDER" fi EXTRA_DOCKER_ARGS="-v $SRC_DIR/$benchmark.db:/benchbase/profiles/sqlite/$benchmark.db" + + if [ "$benchmark" == 'templated' ]; then + # See notes below: + EXTRA_DOCKER_ARGS+=" -v $SRC_DIR/$benchmark.db:/benchbase/profiles/sqlite/tpcc.db" + fi else if [ ! -x "docker/${BENCHBASE_PROFILE}-${PROFILE_VERSION}/up.sh" ]; then echo "ERROR: No docker up.sh script available for '$BENCHBASE_PROFILE'" @@ -38,6 +43,25 @@ fi CREATE_DB_ARGS='--create=true --load=true' if [ "${SKIP_LOAD_DB:-false}" == 'true' ]; then CREATE_DB_ARGS='' +elif [ "$benchmark" == 'templated' ]; then + # For templated benchmarks, we need to preload some data for the test since by + # design, templated benchmarks do not support the 'load' operation + # In this case, we load the tpcc data. + echo "INFO: Loading tpcc data for templated benchmark" + if [ "$BENCHBASE_PROFILE" == 'sqlite' ]; then + # Sqlite will load much faster if we disable sync. + tpcc_config="config/sample_tpcc_nosync_config.xml" + else + tpcc_config="config/sample_tpcc_config.xml" + fi + SKIP_TESTS=${SKIP_TESTS:-true} EXTRA_DOCKER_ARGS="--network=host $EXTRA_DOCKER_ARGS" \ + ./docker/benchbase/run-full-image.sh \ + --config "$tpcc_config" --bench tpcc \ + $CREATE_DB_ARGS --execute=false + + # Mark those actions as completed. + CREATE_DB_ARGS='' + SKIP_TESTS=true fi SKIP_TESTS=${SKIP_TESTS:-true} EXTRA_DOCKER_ARGS="--network=host $EXTRA_DOCKER_ARGS" \ diff --git a/pom.xml b/pom.xml index c6671bf0c..b2b486d36 100644 --- a/pom.xml +++ b/pom.xml @@ -241,6 +241,19 @@ test + + org.immutables + value + 2.9.0 + provided + + + + org.codehaus.janino + janino + 3.1.9 + + diff --git a/src/main/java/com/oltpbenchmark/DBWorkload.java b/src/main/java/com/oltpbenchmark/DBWorkload.java index dfc9beb5a..15b05fddc 100644 --- a/src/main/java/com/oltpbenchmark/DBWorkload.java +++ b/src/main/java/com/oltpbenchmark/DBWorkload.java @@ -496,8 +496,7 @@ private static Options buildOptions(XMLConfiguration pluginConfig) { return options; } - private static XMLConfiguration buildConfiguration(String filename) throws ConfigurationException { - + public static XMLConfiguration buildConfiguration(String filename) throws ConfigurationException { Parameters params = new Parameters(); FileBasedConfigurationBuilder builder = new FileBasedConfigurationBuilder<>(XMLConfiguration.class) .configure(params.xml() @@ -505,7 +504,6 @@ private static XMLConfiguration buildConfiguration(String filename) throws Confi .setListDelimiterHandler(new DisabledListDelimiterHandler()) .setExpressionEngine(new XPathExpressionEngine())); return builder.getConfiguration(); - } private static void writeHistograms(Results r) { diff --git a/src/main/java/com/oltpbenchmark/api/BenchmarkModule.java b/src/main/java/com/oltpbenchmark/api/BenchmarkModule.java index ddd0a6cc0..247546483 100644 --- a/src/main/java/com/oltpbenchmark/api/BenchmarkModule.java +++ b/src/main/java/com/oltpbenchmark/api/BenchmarkModule.java @@ -48,6 +48,11 @@ public abstract class BenchmarkModule { */ protected final WorkloadConfiguration workConf; + /** + * Class loader variable for this benchmark + */ + protected ClassLoader classLoader; + /** * These are the variations of the Procedure's Statement SQL */ @@ -72,6 +77,15 @@ public abstract class BenchmarkModule { public BenchmarkModule(WorkloadConfiguration workConf) { this.workConf = workConf; this.dialects = new StatementDialects(workConf); + initClassLoader(); + } + + /** + * Instantiates the classLoader variable, needs to be overwritten if + * benchmark uses a custom implementation. + */ + protected void initClassLoader() { + this.classLoader = ClassLoader.getSystemClassLoader(); } // -------------------------------------------------------------------------- @@ -331,7 +345,7 @@ public final TransactionType initTransactionType(String procName, int id, long p Package pkg = this.getProcedurePackageImpl(); String fullName = pkg.getName() + "." + procName; - Class procClass = (Class) ClassUtil.getClass(fullName); + Class procClass = (Class) ClassUtil.getClass(this.classLoader, fullName); return new TransactionType(procClass, id, false, preExecutionWait, postExecutionWait); } diff --git a/src/main/java/com/oltpbenchmark/api/templates/ObjectFactory.java b/src/main/java/com/oltpbenchmark/api/templates/ObjectFactory.java new file mode 100644 index 000000000..82cbd4b96 --- /dev/null +++ b/src/main/java/com/oltpbenchmark/api/templates/ObjectFactory.java @@ -0,0 +1,88 @@ +/* + * Copyright 2020 by OLTPBenchmark Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// +// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, vJAXB 2.1.10 +// See http://java.sun.com/xml/jaxb +// Any modifications to this file will be lost upon recompilation of the source schema. +// Generated on: 2011.12.28 at 11:42:38 PM EST +// + + +package com.oltpbenchmark.api.templates; + +import jakarta.xml.bind.JAXBElement; +import jakarta.xml.bind.annotation.XmlElementDecl; +import jakarta.xml.bind.annotation.XmlRegistry; +import javax.xml.namespace.QName; + + +/** + * This object contains factory methods for each + * Java content interface and Java element interface + * generated in the com.oltpbenchmark.api.templates package. + *

An ObjectFactory allows you to programatically + * construct new instances of the Java representation + * for XML content. The Java representation of XML + * content can consist of schema derived interfaces + * and classes representing the binding of schema + * type definitions, element declarations and model + * groups. Factory methods for each of these are + * provided in this class. + */ +@XmlRegistry +public class ObjectFactory { + + private final static QName _Templates_QNAME = new QName("", "templates"); + + /** + * Create a new ObjectFactory that can be used to create new instances of + * schema derived classes for package: com.oltpbenchmark.api.templates + */ + public ObjectFactory() { + } + + /** + * Create an instance of {@link TemplateType } + */ + public TemplateType createTemplateType() { + return new TemplateType(); + } + + /** + * Create an instance of {@link TypesType } + */ + public TypesType createTypesType() { + return new TypesType(); + } + + /** + * Create an instance of {@link ValuesType } + */ + public ValuesType createValuesType() { + return new ValuesType(); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link TemplatesType }{@code >}} + */ + @XmlElementDecl(namespace = "", name = "templates") + public JAXBElement createDialects(TemplatesType value) { + return new JAXBElement<>(_Templates_QNAME, TemplatesType.class, null, value); + } + +} diff --git a/src/main/java/com/oltpbenchmark/api/templates/TemplateType.java b/src/main/java/com/oltpbenchmark/api/templates/TemplateType.java new file mode 100644 index 000000000..1bcba2164 --- /dev/null +++ b/src/main/java/com/oltpbenchmark/api/templates/TemplateType.java @@ -0,0 +1,102 @@ +/* + * Copyright 2020 by OLTPBenchmark Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// +// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, vJAXB 2.1.10 +// See http://java.sun.com/xml/jaxb +// Any modifications to this file will be lost upon recompilation of the source schema. +// Generated on: 2011.12.28 at 11:42:38 PM EST +// + + +package com.oltpbenchmark.api.templates; + +import jakarta.xml.bind.annotation.*; +import java.util.ArrayList; +import java.util.List; + + +/** + *

Java class for dialectType complex type. + * + *

The following schema fragment specifies the expected content contained within this class. + * + *

+ * <complexType name="dialectType">
+ *   <complexContent>
+ *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *       <sequence>
+ *         <element name="procedure" type="{}procedureType" maxOccurs="unbounded"/>
+ *       </sequence>
+ *       <attribute name="type" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *     </restriction>
+ *   </complexContent>
+ * </complexType>
+ * 
+ */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "templateType", propOrder = { + "query", "types", "values" +}) +public class TemplateType { + + @XmlAttribute(required = true) + protected String name; + @XmlElement(required = true) + protected String query; + @XmlElement(required = true) + protected TypesType types; + @XmlElement(required = true) + protected List values; + + /** + * Gets the value of the query property. + */ + public String getQuery() { + return this.query; + } + + + /** + * Gets the value of the types property. + */ + public TypesType getTypes() { + return this.types; + } + + /** + * Gets the value of the types property. + * + * Objects of the following type(s) are allowed in the list {@link ValuesType } + */ + public List getValues() { + if (this.values == null) { + this.values = new ArrayList<>(); + } + return this.values; + } + + /** + * Gets the value of the name property. + * + * @return possible object is {@link String } + */ + public String getName() { + return name; + } + +} diff --git a/src/main/java/com/oltpbenchmark/api/templates/TemplatesType.java b/src/main/java/com/oltpbenchmark/api/templates/TemplatesType.java new file mode 100644 index 000000000..6910f5477 --- /dev/null +++ b/src/main/java/com/oltpbenchmark/api/templates/TemplatesType.java @@ -0,0 +1,74 @@ +/* + * Copyright 2020 by OLTPBenchmark Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// +// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, vJAXB 2.1.10 +// See http://java.sun.com/xml/jaxb +// Any modifications to this file will be lost upon recompilation of the source schema. +// Generated on: 2011.12.28 at 11:42:38 PM EST +// + + +package com.oltpbenchmark.api.templates; + +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlElement; +import jakarta.xml.bind.annotation.XmlType; +import java.util.ArrayList; +import java.util.List; + + +/** + *

Java class for dialectsType complex type. + * + *

The following schema fragment specifies the expected content contained within this class. + * + *

+ * <complexType name="dialectsType">
+ *   <complexContent>
+ *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *       <sequence>
+ *         <element name="dialect" type="{}dialectType" maxOccurs="unbounded"/>
+ *       </sequence>
+ *     </restriction>
+ *   </complexContent>
+ * </complexType>
+ * 
+ */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "templatesType", propOrder = { + "template" +}) +public class TemplatesType { + + @XmlElement(required = true) + protected List template; + + /** + * Gets the value of the dialect property. + * + * Objects of the following type(s) are allowed in the list {@link TemplateType } + */ + public List getTemplateList() { + if (this.template == null) { + this.template = new ArrayList<>(); + } + return this.template; + } + +} diff --git a/src/main/java/com/oltpbenchmark/api/templates/TypesType.java b/src/main/java/com/oltpbenchmark/api/templates/TypesType.java new file mode 100644 index 000000000..170844d78 --- /dev/null +++ b/src/main/java/com/oltpbenchmark/api/templates/TypesType.java @@ -0,0 +1,70 @@ +/* + * Copyright 2020 by OLTPBenchmark Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// +// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, vJAXB 2.1.10 +// See http://java.sun.com/xml/jaxb +// Any modifications to this file will be lost upon recompilation of the source schema. +// Generated on: 2011.12.28 at 11:42:38 PM EST +// + + +package com.oltpbenchmark.api.templates; + +import jakarta.xml.bind.annotation.*; +import java.util.ArrayList; +import java.util.List; + + +/** + *

Java class for dialectType complex type. + * + *

The following schema fragment specifies the expected content contained within this class. + * + *

+ * <complexType name="parameterTypesType">
+ *   <complexContent>
+ *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *       <sequence>
+ *         <element name="parameters_type" type="{}procedureType" maxOccurs="unbounded"/>
+ *       </sequence>
+ *     </restriction>
+ *   </complexContent>
+ * </complexType>
+ * 
+ */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "typesType", propOrder = {"type"}) +public class TypesType { + + @XmlElement(required = true) + protected List type; + + + /** + * Gets the value of the type property. + * + * Objects of the following type(s) are allowed in the list {@link String } + */ + public List getTypeList() { + if (this.type == null) { + this.type = new ArrayList<>(); + } + return this.type; + } + +} diff --git a/src/main/java/com/oltpbenchmark/api/templates/ValuesType.java b/src/main/java/com/oltpbenchmark/api/templates/ValuesType.java new file mode 100644 index 000000000..e2120bb14 --- /dev/null +++ b/src/main/java/com/oltpbenchmark/api/templates/ValuesType.java @@ -0,0 +1,70 @@ +/* + * Copyright 2020 by OLTPBenchmark Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// +// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, vJAXB 2.1.10 +// See http://java.sun.com/xml/jaxb +// Any modifications to this file will be lost upon recompilation of the source schema. +// Generated on: 2011.12.28 at 11:42:38 PM EST +// + + +package com.oltpbenchmark.api.templates; + +import jakarta.xml.bind.annotation.*; +import java.util.ArrayList; +import java.util.List; + + +/** + *

Java class for dialectType complex type. + * + *

The following schema fragment specifies the expected content contained within this class. + * + *

+ * <complexType name="parameterTypesType">
+ *   <complexContent>
+ *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *       <sequence>
+ *         <element name="parameters_type" type="{}procedureType" maxOccurs="unbounded"/>
+ *       </sequence>
+ *     </restriction>
+ *   </complexContent>
+ * </complexType>
+ * 
+ */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "valuesType", propOrder = {"value"}) +public class ValuesType { + + @XmlElement(required = true) + protected List value; + + + /** + * Gets the value of the value property. + * + * Objects of the following type(s) are allowed in the list {@link String } + */ + public List getValueList() { + if (this.value == null) { + this.value = new ArrayList<>(); + } + return this.value; + } + +} diff --git a/src/main/java/com/oltpbenchmark/benchmarks/templated/README.md b/src/main/java/com/oltpbenchmark/benchmarks/templated/README.md new file mode 100644 index 000000000..38d919f69 --- /dev/null +++ b/src/main/java/com/oltpbenchmark/benchmarks/templated/README.md @@ -0,0 +1,56 @@ +# Templated Benchmarks + +This class is used to execute templated benchmarks, i.e., benchmark queries that have parameters that the user wants to set dynamically. +A templated benchmark config has the following structure: + +```xml + + + + +``` + +where `$ParameterType` is the `java.sql.Types` value (i.e., Integer, Boolean, etc.) and each value tag within `values` contains the values for one instantiation of the parameters set in `$SQLQuery`. +The SQL query string is read as a `PreparedStatement`, i.e., parameters are defined in the string via a `?` placeholder. + +An example for a templated benchmark can be found in [`data/templated/example.xml`](../../../../../../../data/templated/example.xml). +The file path for the XML template has to be defined in the workload configuration using the `templates_file` tag. + +An example configuration can be found in [`config/sqlserver/sample_template_config.xml`](../../../../../../../config/sqlserver/sample_templated_config.xml). + +> Since the templated benchmark is meant to flexibly support a myriad of different database schemas, it doesn't *currently* support the `init` and `load` phases. + + +The example can be executed if a loaded TPC-C instance is used as JDBC endpoint. + +For instance: + +```sh +java -jar benchbase.jar -b tpcc -c config/sqlserver/sample_tpcc_config.xml --create=true --load=true --execute=false +``` + +Templated benchmarks are instantiated using `templated` as benchmark class when running BenchBase via the command line. + +For instance: + +```sh +java -jar benchbase.jar -b templated -c config/sqlserver/sample_templated_config.xml --create=false --load=false --execute=true --json-histograms results/histograms.json +``` + +> For additional examples, please refer to the build pipeline definition in the [`maven.yml`](../../../../../../../.github/workflows/maven.yml#L423) Github Actions workflow file. diff --git a/src/main/java/com/oltpbenchmark/benchmarks/templated/TemplatedBenchmark.java b/src/main/java/com/oltpbenchmark/benchmarks/templated/TemplatedBenchmark.java new file mode 100644 index 000000000..01ed0ec91 --- /dev/null +++ b/src/main/java/com/oltpbenchmark/benchmarks/templated/TemplatedBenchmark.java @@ -0,0 +1,280 @@ +/* + * Copyright 2020 by OLTPBenchmark Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package com.oltpbenchmark.benchmarks.templated; + +import java.io.FileInputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import javax.xml.transform.stream.StreamSource; +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; + +import org.apache.commons.text.StringEscapeUtils; +import org.codehaus.commons.compiler.CompilerFactoryFactory; +import org.codehaus.commons.compiler.ICompilerFactory; +import org.codehaus.commons.compiler.ISimpleCompiler; +import org.immutables.value.Value; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.oltpbenchmark.WorkloadConfiguration; +import com.oltpbenchmark.api.BenchmarkModule; +import com.oltpbenchmark.api.Loader; +import com.oltpbenchmark.api.Procedure; +import com.oltpbenchmark.api.SQLStmt; +import com.oltpbenchmark.api.TransactionType; +import com.oltpbenchmark.api.Worker; +import com.oltpbenchmark.api.templates.ValuesType; +import com.oltpbenchmark.api.templates.TemplateType; +import com.oltpbenchmark.api.templates.TemplatesType; +import com.oltpbenchmark.benchmarks.templated.procedures.GenericQuery; +import com.oltpbenchmark.benchmarks.templated.procedures.GenericQuery.QueryTemplateInfo; +import com.oltpbenchmark.benchmarks.templated.util.GenericQueryOperation; +import com.oltpbenchmark.benchmarks.templated.util.TraceTransactionGenerator; +import com.opencsv.CSVParser; +import com.opencsv.CSVParserBuilder; + +import jakarta.xml.bind.JAXBContext; +import jakarta.xml.bind.JAXBElement; +import jakarta.xml.bind.Unmarshaller; + +/** + * This class is used to execute templated benchmarks, i.e., benchmarks that + * have parameters that the user wants to set dynamically. More information + * about the structure of the expected template can be found in the local + * readme file. + */ +public class TemplatedBenchmark extends BenchmarkModule { + private static final Logger LOG = LoggerFactory.getLogger(TemplatedBenchmark.class); + + public TemplatedBenchmark(WorkloadConfiguration workConf) { + super(workConf); + } + + @Override + protected void initClassLoader() { + super.initClassLoader(); + + if (workConf != null && workConf.getXmlConfig().containsKey("query_templates_file")) { + this.classLoader = this.loadQueryTemplates( + workConf.getXmlConfig().getString("query_templates_file")); + } else { + LOG.error("No query_templates_file specified in xml config."); + } + } + + @Override + protected Package getProcedurePackageImpl() { + return (GenericQuery.class.getPackage()); + } + + private CustomClassLoader getCustomClassLoader() { + return (CustomClassLoader) this.classLoader; + } + + public List> getProcedureClasses() { + return this.getCustomClassLoader().getProcedureClasses(); + } + + @Override + protected List> makeWorkersImpl() { + List> workers = new ArrayList<>(); + + try { + final Map, TraceTransactionGenerator> generators = new HashMap<>(); + // Create potential parameter bindings for each template. Add those + // to a trace transaction generator that will determine how the + // parameters are used. + for (Entry kv : getProcedures().entrySet()) { + // Sanity check that the procedure has the right type. + if (!(kv.getValue() instanceof GenericQuery)) { + LOG.error( + String.format( + "Procedure %s does not have the correct class type (GenericQuery).", + kv.getValue().toString())); + continue; + } + GenericQuery proc = (GenericQuery) kv.getValue(); + QueryTemplateInfo info = proc.getQueryTemplateInfo(); + + // Parse parameter values and add each combination to a generator. + // FIXME: This method does not currently support NULLable + // parameters since they will be parsed as an empty string. + // See Also: comments in GenericQuery.getStatement() + // Additionally, it's somewhat unnecessarily expensive, since + // we convert from XML represented values back to CSV separated + // list of params. + List list = new ArrayList<>(); + String[] paramsTypes = info.getParamsTypes(); + CSVParser parser = new CSVParserBuilder() + .withQuoteChar('\'') + .build(); + for (String binding : info.getParamsValues()) { + Object[] params = parser.parseLine(binding); + assert paramsTypes.length == params.length; + list.add(new GenericQueryOperation(params)); + } + generators.put(proc.getClass(), new TraceTransactionGenerator(list)); + } + + // Create workers. + int numTerminals = workConf.getTerminals(); + LOG.info(String.format("Creating %d workers for templated benchmark", numTerminals)); + for (int i = 0; i < numTerminals; i++) { + workers.add(new TemplatedWorker(this, i, generators)); + } + } catch (Exception e) { + throw new IllegalStateException("Unable to create workers", e); + } + return workers; + } + + @Override + protected Loader makeLoaderImpl() { + throw new UnsupportedOperationException("Templated benchmarks do not currently support loading directly."); + } + + private CustomClassLoader loadQueryTemplates(String file) { + // Instantiate Java compiler. + CustomClassLoader ccloader = new CustomClassLoader(this.classLoader); + try { + // Parse template file. + final ICompilerFactory compilerFactory = CompilerFactoryFactory.getDefaultCompilerFactory( + TemplatedBenchmark.class.getClassLoader()); + + JAXBContext jc = JAXBContext.newInstance("com.oltpbenchmark.api.templates"); + SchemaFactory sf = SchemaFactory.newInstance(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI); + Schema schema = sf.newSchema(new StreamSource(this.getClass().getResourceAsStream("/templates.xsd"))); + Unmarshaller unmarshaller = jc.createUnmarshaller(); + unmarshaller.setSchema(schema); + + StreamSource streamSource = new StreamSource(new FileInputStream(file)); + JAXBElement result = unmarshaller.unmarshal(streamSource, TemplatesType.class); + TemplatesType templates = result.getValue(); + + for (TemplateType template : templates.getTemplateList()) { + ImmutableParsedQueryTemplate.Builder b = ImmutableParsedQueryTemplate.builder(); + b.name(template.getName()); + b.query(template.getQuery()); + b.paramsTypes(template.getTypes().getTypeList()); + for (ValuesType paramValue : template.getValues()) { + b.addParamsValues(String.join(",", paramValue.getValueList())); + } + + ParsedQueryTemplate qt = b.build(); + // Create and compile class. + final String s = """ + package %s ; + public final class %s extends %s { + @Override + public %s getQueryTemplateInfo() { + return ImmutableQueryTemplateInfo.builder() + .query(new %s(\"%s\")) + .paramsTypes(new String[] {%s}) + .paramsValues(new String[] {%s}) + .build(); + } + } + """.formatted( + GenericQuery.class.getPackageName(), + qt.getName(), + GenericQuery.class.getCanonicalName(), + QueryTemplateInfo.class.getCanonicalName(), + SQLStmt.class.getCanonicalName(), + StringEscapeUtils.escapeJava(qt.getQuery()), + getParamsString(qt.getParamsTypes()), + getParamsString(qt.getParamsValues())); + LOG.debug("Class definition for query template {}:\n {}", qt.getName(), s); + final String qualifiedClassName = GenericQuery.class.getPackageName() + "." + qt.getName(); + final ISimpleCompiler compiler = compilerFactory.newSimpleCompiler(); + compiler.setTargetVersion(17); + compiler.setParentClassLoader(this.classLoader); + compiler.cook(s); + ccloader.putClass(qualifiedClassName, + compiler.getClassLoader().loadClass(qualifiedClassName)); + } + } catch (Exception e) { + throw new IllegalStateException("Unable to load query templates", e); + } + return ccloader; + } + + private String getParamsString(List params) { + String result = ""; + for (String param : params) { + result += "\"" + StringEscapeUtils.escapeJava(param) + "\","; + } + return result.isEmpty() ? "" : result.substring(0, result.length() - 1); + } + + private static class CustomClassLoader extends ClassLoader { + + private final Map> classes = new HashMap<>(); + + private CustomClassLoader(ClassLoader parent) { + super(parent); + } + + @Override + public Class findClass(String name) throws ClassNotFoundException { + Class clazz = classes.get(name); + return clazz != null ? clazz : super.findClass(name); + } + + public void putClass(String name, Class clazz) { + classes.put(name, clazz); + } + + @SuppressWarnings("unchecked") + public List> getProcedureClasses() { + List> result = new ArrayList<>(); + for (Class clz : classes.values()) { + if (Procedure.class.isAssignableFrom(clz)) { + result.add((Class) clz); + } + } + return result; + } + } + + @Value.Immutable + public interface ParsedQueryTemplate { + + /** Template name. */ + String getName(); + + /** Query string for this template. */ + String getQuery(); + + /** Potential query parameter types. */ + @Value.Default + default List getParamsTypes() { + return List.of(); + } + + /** Potential query parameter values. */ + @Value.Default + default List getParamsValues() { + return List.of(); + } + } + +} diff --git a/src/main/java/com/oltpbenchmark/benchmarks/templated/TemplatedWorker.java b/src/main/java/com/oltpbenchmark/benchmarks/templated/TemplatedWorker.java new file mode 100644 index 000000000..c1fcdfc7e --- /dev/null +++ b/src/main/java/com/oltpbenchmark/benchmarks/templated/TemplatedWorker.java @@ -0,0 +1,66 @@ +/* + * Copyright 2020 by OLTPBenchmark Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package com.oltpbenchmark.benchmarks.templated; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Map; + +import com.oltpbenchmark.api.Procedure; +import com.oltpbenchmark.api.Procedure.UserAbortException; +import com.oltpbenchmark.benchmarks.templated.procedures.GenericQuery; +import com.oltpbenchmark.benchmarks.templated.util.TraceTransactionGenerator; +import com.oltpbenchmark.api.TransactionType; +import com.oltpbenchmark.api.Worker; +import com.oltpbenchmark.types.TransactionStatus; + +public class TemplatedWorker extends Worker { + + protected final Map, TraceTransactionGenerator> generators; + + public TemplatedWorker(TemplatedBenchmark benchmarkModule, int id, + Map, TraceTransactionGenerator> generators) { + super(benchmarkModule, id); + this.rng().setSeed(benchmarkModule.getWorkloadConfiguration().getRandomSeed()); + this.generators = generators; + } + + @Override + protected TransactionStatus executeWork(Connection conn, TransactionType nextTransaction) + throws UserAbortException, SQLException { + try { + Class clazz = nextTransaction.getProcedureClass(); + GenericQuery proc = (GenericQuery) this.getProcedure(clazz); + if (!generators.get(clazz).isEmpty()) { + // If there is a generator available use it to create a + // parameter binding. + TraceTransactionGenerator generator = generators.get(clazz); + proc.run(conn, generator.nextTransaction().getParams()); + } else { + // If the generator has no transactions, there are no parameters. + proc.run(conn); + } + + } catch (ClassCastException e) { + throw new RuntimeException(e); + } + + return (TransactionStatus.SUCCESS); + + } +} + diff --git a/src/main/java/com/oltpbenchmark/benchmarks/templated/procedures/GenericQuery.java b/src/main/java/com/oltpbenchmark/benchmarks/templated/procedures/GenericQuery.java new file mode 100644 index 000000000..2d5df5957 --- /dev/null +++ b/src/main/java/com/oltpbenchmark/benchmarks/templated/procedures/GenericQuery.java @@ -0,0 +1,99 @@ +/* + * Copyright 2020 by OLTPBenchmark Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package com.oltpbenchmark.benchmarks.templated.procedures; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import java.util.List; + +import org.immutables.value.Value; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.oltpbenchmark.api.Procedure; +import com.oltpbenchmark.api.SQLStmt; + +public abstract class GenericQuery extends Procedure { + + protected static final Logger LOG = LoggerFactory.getLogger(GenericQuery.class); + + /** Execution method with parameters. */ + public void run(Connection conn, List params) throws SQLException { + try (PreparedStatement stmt = getStatement(conn, params); ResultSet rs = stmt.executeQuery()) { + while (rs.next()) { + //do nothing + } + } + conn.commit(); + } + + /** Execution method without parameters. */ + public void run(Connection conn) throws SQLException { + QueryTemplateInfo queryTemplateInfo = this.getQueryTemplateInfo(); + + try (PreparedStatement stmt = this.getPreparedStatement(conn, queryTemplateInfo.getQuery()); ResultSet rs = stmt.executeQuery()) { + while (rs.next()) { + //do nothing + } + } + conn.commit(); + } + + public PreparedStatement getStatement(Connection conn, List params) throws SQLException { + QueryTemplateInfo queryTemplateInfo = this.getQueryTemplateInfo(); + + PreparedStatement stmt = this.getPreparedStatement(conn, queryTemplateInfo.getQuery()); + String[] paramsTypes = queryTemplateInfo.getParamsTypes(); + for (int i = 0; i < paramsTypes.length; i++) { + if (paramsTypes[i].equalsIgnoreCase("NULL")) { + stmt.setNull(i + 1, Types.NULL); + } else { + try { + // TODO: add support for nullable other types + // For instance, can we provide a tag in the XML file to represent a NULL value? + // Or does it need a special marker like "$null" to signify a NULL value? + Object param = params.get(i); + stmt.setObject(i + 1, param, Integer.parseInt(Types.class.getDeclaredField(paramsTypes[i]).get(null).toString())); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException( + "Error when setting parameters. Parameter type: " + paramsTypes[i] + ", parameter value: " + params.get(i)); + } + } + } + return stmt; + } + + public abstract QueryTemplateInfo getQueryTemplateInfo(); + + @Value.Immutable + public interface QueryTemplateInfo { + + /** Query string for this template. */ + SQLStmt getQuery(); + + /** Query parameter types. */ + String[] getParamsTypes(); + + /** Potential query parameter values. */ + String[] getParamsValues(); + } + +} diff --git a/src/main/java/com/oltpbenchmark/benchmarks/templated/util/GenericQueryOperation.java b/src/main/java/com/oltpbenchmark/benchmarks/templated/util/GenericQueryOperation.java new file mode 100644 index 000000000..b2a3cdc73 --- /dev/null +++ b/src/main/java/com/oltpbenchmark/benchmarks/templated/util/GenericQueryOperation.java @@ -0,0 +1,41 @@ +/* + * Copyright 2020 by OLTPBenchmark Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package com.oltpbenchmark.benchmarks.templated.util; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import com.oltpbenchmark.api.Operation; + +/** + * Immutable class containing information about transactions. + */ +public class GenericQueryOperation extends Operation { + + public final List params; + + + public GenericQueryOperation(Object[] params) { + super(); + this.params = Collections.unmodifiableList(Arrays.asList(params)); + } + + public List getParams() { + return params; + } +} diff --git a/src/main/java/com/oltpbenchmark/benchmarks/templated/util/TraceTransactionGenerator.java b/src/main/java/com/oltpbenchmark/benchmarks/templated/util/TraceTransactionGenerator.java new file mode 100644 index 000000000..782df565d --- /dev/null +++ b/src/main/java/com/oltpbenchmark/benchmarks/templated/util/TraceTransactionGenerator.java @@ -0,0 +1,47 @@ +/* + * Copyright 2020 by OLTPBenchmark Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package com.oltpbenchmark.benchmarks.templated.util; + +import java.util.Collections; +import java.util.List; + +import com.oltpbenchmark.api.TransactionGenerator; +import com.oltpbenchmark.distributions.CyclicCounterGenerator; + +public class TraceTransactionGenerator implements TransactionGenerator { + + private final List transactions; + private final CyclicCounterGenerator nextInTrace; + + /** + * @param transactions a list of transactions shared between threads. + */ + public TraceTransactionGenerator(List transactions) { + this.transactions = Collections.unmodifiableList(transactions); + this.nextInTrace = new CyclicCounterGenerator(transactions.size()); + } + + @Override + public GenericQueryOperation nextTransaction() { + return transactions.get(nextInTrace.nextInt()); + } + + public boolean isEmpty() { + return transactions.size() == 0; + } + +} diff --git a/src/main/java/com/oltpbenchmark/distributions/CyclicCounterGenerator.java b/src/main/java/com/oltpbenchmark/distributions/CyclicCounterGenerator.java new file mode 100644 index 000000000..3b6ec4902 --- /dev/null +++ b/src/main/java/com/oltpbenchmark/distributions/CyclicCounterGenerator.java @@ -0,0 +1,63 @@ +/* + * Copyright 2020 by OLTPBenchmark Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package com.oltpbenchmark.distributions; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Thread-safe cyclic counter generator. + */ +public class CyclicCounterGenerator extends IntegerGenerator { + + private final int maxVal; + private final AtomicInteger counter; + + public CyclicCounterGenerator(int maxVal) { + this.maxVal = maxVal; + this.counter = new AtomicInteger(0); + } + + protected void setLastInt(int last) { + throw new UnsupportedOperationException("Cyclic counter cannot be set to a value"); + } + + @Override + public int nextInt() { + return counter.accumulateAndGet(1, (index, inc) -> (++index >= maxVal ? 0 : index)); + } + + @Override + public String nextString() { + return "" + nextInt(); + } + + @Override + public String lastString() { + return "" + lastInt(); + } + + @Override + public int lastInt() { + return counter.get(); + } + + @Override + public double mean() { + throw new UnsupportedOperationException("Not implemented yet"); + } + +} diff --git a/src/main/java/com/oltpbenchmark/util/ClassUtil.java b/src/main/java/com/oltpbenchmark/util/ClassUtil.java index 0dffaedbb..9963b6a33 100644 --- a/src/main/java/com/oltpbenchmark/util/ClassUtil.java +++ b/src/main/java/com/oltpbenchmark/util/ClassUtil.java @@ -186,11 +186,23 @@ public static Constructor getConstructor(Class target_class, Class. * @return */ public static Class getClass(String class_name) { + return getClass(ClassLoader.getSystemClassLoader(), class_name); + } + + /** + * @param loader + * @param class_name + * @return + */ + public static Class getClass(ClassLoader loader, String class_name) { + Class target_class = null; try { - return ClassUtils.getClass(class_name); - } catch (ClassNotFoundException e) { - throw new RuntimeException(e); + target_class = ClassUtils.getClass(loader, class_name); + } catch (Exception ex) { + throw new RuntimeException("Failed to retrieve class for " + class_name, ex); } + return target_class; + } } diff --git a/src/main/java/com/oltpbenchmark/util/SQLUtil.java b/src/main/java/com/oltpbenchmark/util/SQLUtil.java index 3471dda1b..c0cd7e8c9 100644 --- a/src/main/java/com/oltpbenchmark/util/SQLUtil.java +++ b/src/main/java/com/oltpbenchmark/util/SQLUtil.java @@ -481,7 +481,6 @@ public static String selectColValues(DatabaseType databaseType, Table catalog_tb public static AbstractCatalog getCatalog(BenchmarkModule benchmarkModule, DatabaseType databaseType, Connection connection) throws SQLException { switch (databaseType) { case NOISEPAGE: // fall-through - case SQLITE: case HSQLDB: return getCatalogHSQLDB(benchmarkModule); default: diff --git a/src/main/resources/templates.xsd b/src/main/resources/templates.xsd new file mode 100644 index 000000000..a2bdf7763 --- /dev/null +++ b/src/main/resources/templates.xsd @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/java/com/oltpbenchmark/api/AbstractTestCase.java b/src/test/java/com/oltpbenchmark/api/AbstractTestCase.java index 5afb3aa73..d09a1d366 100644 --- a/src/test/java/com/oltpbenchmark/api/AbstractTestCase.java +++ b/src/test/java/com/oltpbenchmark/api/AbstractTestCase.java @@ -72,12 +72,14 @@ public abstract class AbstractTestCase { public AbstractTestCase(boolean createDatabase, boolean loadDatabase) { + this.benchmark = null; this.createDatabase = createDatabase; this.loadDatabase = loadDatabase; this.ddlOverridePath = null; } public AbstractTestCase(boolean createDatabase, boolean loadDatabase, String ddlOverridePath) { + this.benchmark = null; this.createDatabase = createDatabase; this.loadDatabase = loadDatabase; this.ddlOverridePath = ddlOverridePath; @@ -112,17 +114,10 @@ public final void setUp() throws Exception { server.start(); this.workConf = new WorkloadConfiguration(); - TransactionTypes txnTypes = new TransactionTypes(new ArrayList<>()); - - int id = 0; - for (Class procedureClass : procedures()) { - TransactionType tt = new TransactionType(procedureClass, id++, false, 0, 0); - txnTypes.add(tt); - } String DB_CONNECTION = String.format("jdbc:hsqldb:hsql://localhost:%d/benchbase", server.getPort()); - this.workConf.setTransTypes(txnTypes); + this.workConf.setTransTypes(proceduresToTransactionTypes(procedures())); this.workConf.setDatabaseType(DB_TYPE); this.workConf.setUrl(DB_CONNECTION); this.workConf.setScaleFactor(DB_SCALE_FACTOR); @@ -138,6 +133,12 @@ public final void setUp() throws Exception { new Class[]{WorkloadConfiguration.class}); assertNotNull(this.benchmark); + // HACK: calling this a second time is a cheap no-op for most benchmark + // tests, but actually ensures that the procedures list is populated + // for the TestTemplatedWorker test which doesn't know its procedures + // until after the benchmark is initialized and the config is loaded. + assertNotNull(this.procedures()); + this.conn = this.benchmark.makeConnection(); assertNotNull(this.conn); @@ -146,23 +147,11 @@ public final void setUp() throws Exception { assertNotNull(this.catalog); if (createDatabase) { - try { - this.benchmark.createDatabase(); - } catch (Exception e) { - LOG.error(e.getMessage(), e); - cleanupServer(); - fail("createDatabase() failed"); - } + this.createDatabase(); } if (loadDatabase) { - try { - this.benchmark.loadDatabase(); - } catch (Exception e) { - LOG.error(e.getMessage(), e); - cleanupServer(); - fail("loadDatabase() failed"); - } + this.loadDatabase(); } try { @@ -174,6 +163,38 @@ public final void setUp() throws Exception { } } + protected TransactionTypes proceduresToTransactionTypes(List> procedures) { + TransactionTypes txnTypes = new TransactionTypes(new ArrayList<>()); + + int id = 0; + for (Class procedureClass : procedures) { + TransactionType tt = new TransactionType(procedureClass, id++, false, 0, 0); + txnTypes.add(tt); + } + + return txnTypes; + } + + protected void createDatabase() { + try { + this.benchmark.createDatabase(); + } catch (Exception e) { + LOG.error(e.getMessage(), e); + cleanupServer(); + fail("createDatabase() failed"); + } + } + + protected void loadDatabase() { + try { + this.benchmark.loadDatabase(); + } catch (Exception e) { + LOG.error(e.getMessage(), e); + cleanupServer(); + fail("loadDatabase() failed"); + } + } + protected void customWorkloadConfiguration(WorkloadConfiguration workConf) { } @@ -193,7 +214,7 @@ public final void tearDown() throws Exception { cleanupServer(); } - private void cleanupServer() { + protected void cleanupServer() { if (server != null) { LOG.trace("shutting down catalogs..."); diff --git a/src/test/java/com/oltpbenchmark/api/AbstractTestWorker.java b/src/test/java/com/oltpbenchmark/api/AbstractTestWorker.java index ed31a07ba..c9a59b751 100644 --- a/src/test/java/com/oltpbenchmark/api/AbstractTestWorker.java +++ b/src/test/java/com/oltpbenchmark/api/AbstractTestWorker.java @@ -39,6 +39,10 @@ public AbstractTestWorker() { super(true, true); } + public AbstractTestWorker(String ddlOverridePath) { + super(true, true, ddlOverridePath); + } + @Override public List ignorableTables() { return null; @@ -97,7 +101,6 @@ public void testExecuteWork() throws Exception { } catch (Throwable ex) { throw new RuntimeException("Failed to execute " + txnType, ex); } finally { - LOG.info("completed execution of [{}] in {} ms", txnType.toString(), sw.getTime(TimeUnit.MILLISECONDS)); } } diff --git a/src/test/java/com/oltpbenchmark/benchmarks/templated/README.md b/src/test/java/com/oltpbenchmark/benchmarks/templated/README.md new file mode 100644 index 000000000..db9b536b3 --- /dev/null +++ b/src/test/java/com/oltpbenchmark/benchmarks/templated/README.md @@ -0,0 +1,9 @@ +# Templated Benchmark Unit Tests + +Note: The templated benchmark does not currently support data loading. + +The current sample config files are built off using the TPC-C data which is expected to be loaded first. + +Hence, in this directory we omit the usual `TestTemplatedBenchmark.java` and `TestTemplatedLoader.java` files. + +To make the unit tests work, we reuse the TPC-C data loader as a part of the `setUp` override. \ No newline at end of file diff --git a/src/test/java/com/oltpbenchmark/benchmarks/templated/TestTemplatedWorker.java b/src/test/java/com/oltpbenchmark/benchmarks/templated/TestTemplatedWorker.java new file mode 100644 index 000000000..19cf7cf99 --- /dev/null +++ b/src/test/java/com/oltpbenchmark/benchmarks/templated/TestTemplatedWorker.java @@ -0,0 +1,123 @@ +package com.oltpbenchmark.benchmarks.templated; + +import com.oltpbenchmark.api.AbstractTestWorker; +import com.oltpbenchmark.api.Procedure; + +import com.oltpbenchmark.DBWorkload; +import com.oltpbenchmark.WorkloadConfiguration; + +import com.oltpbenchmark.benchmarks.tpcc.TPCCBenchmark; + +import java.nio.file.Paths; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.configuration2.XMLConfiguration; +import org.apache.commons.configuration2.ex.ConfigurationException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.junit.Assert.fail; +import static org.junit.Assert.assertNotNull; + + +public class TestTemplatedWorker extends AbstractTestWorker { + private static final Logger LOG = LoggerFactory.getLogger(TestTemplatedWorker.class); + + public static final String DDL_OVERRIDE_PATH = Paths.get("src", "main", "resources", "benchmarks", "tpcc", "ddl-generic.sql").toAbsolutePath().toString(); + public static final String SAMPLE_TEMPLATED_LOADING_CONFIG = Paths.get("config", "sqlite", "sample_tpcc_config.xml").toAbsolutePath().toString(); + public static final String SAMPLE_TEMPLATED_CONFIG = Paths.get("config", "sqlite", "sample_templated_config.xml").toAbsolutePath().toString(); + public static final String TEMPLATES_CONFIG = Paths.get("data", "templated", "example.xml").toAbsolutePath().toString(); + + TPCCBenchmark tpccBenchmark = null; + + public TestTemplatedWorker() { + // Technically we aren't creating this schema with the + // TemplatedBenchmark, but specifying the DDL that we are using (see + // below) allows some other checks to pass. + super(DDL_OVERRIDE_PATH); + } + + public static void setWorkloadConfigXml(WorkloadConfiguration workConf) { + // Load the configuration file so we can parse the query_template_file value. + try { + XMLConfiguration xmlConf = DBWorkload.buildConfiguration(SAMPLE_TEMPLATED_CONFIG); + workConf.setXmlConfig(xmlConf); + } + catch (ConfigurationException ex) { + LOG.error("Error loading configuration: " + SAMPLE_TEMPLATED_CONFIG, ex); + } + } + + @Override + protected void customWorkloadConfiguration(WorkloadConfiguration workConf) { + setWorkloadConfigXml(workConf); + } + + @Override + public List> procedures() { + // Note: the first time this is called is before the benchmark is + // initialized, so it should return nothing. + // It's only populated after the config is loaded for the benchmark. + List> procedures = new ArrayList<>(); + if (this.benchmark != null) { + procedures = this.benchmark.getProcedureClasses(); + if (!procedures.isEmpty() && this.workConf.getTransTypes().isEmpty()) { + workConf.setTransTypes(proceduresToTransactionTypes(procedures)); + } + } + return procedures; + } + + @Override + public Class benchmarkClass() { + return TemplatedBenchmark.class; + } + + private void setupTpccBenchmarkHelper() throws SQLException { + if (this.tpccBenchmark != null) { + return; + } + + // Create a second benchmark to re/ab/use for loading the database (tpcc in this case). + WorkloadConfiguration tpccWorkConf = new WorkloadConfiguration(); + tpccWorkConf.setDatabaseType(this.workConf.getDatabaseType()); + tpccWorkConf.setUrl(this.workConf.getUrl()); + tpccWorkConf.setScaleFactor(this.workConf.getScaleFactor()); + tpccWorkConf.setTerminals(this.workConf.getTerminals()); + tpccWorkConf.setBatchSize(this.workConf.getBatchSize()); + // tpccWorkConf.setBenchmarkName(BenchmarkModule.convertBenchmarkClassToBenchmarkName(TPCCBenchmark.class)); + tpccWorkConf.setBenchmarkName(TPCCBenchmark.class.getSimpleName().toLowerCase().replace("benchmark", "")); + + this.tpccBenchmark = new TPCCBenchmark(this.workConf); + conn = this.tpccBenchmark.makeConnection(); + assertNotNull(conn); + this.tpccBenchmark.refreshCatalog(); + catalog = this.tpccBenchmark.getCatalog(); + assertNotNull(catalog); + } + + protected void createDatabase() { + try { + this.setupTpccBenchmarkHelper(); + this.tpccBenchmark.createDatabase(); + } catch (Exception e) { + LOG.error(e.getMessage(), e); + cleanupServer(); + fail("createDatabase() failed"); + } + } + + protected void loadDatabase() { + try { + this.setupTpccBenchmarkHelper(); + this.tpccBenchmark.loadDatabase(); + } catch (Exception e) { + LOG.error(e.getMessage(), e); + cleanupServer(); + fail("loadDatabase() failed"); + } + } +} From a3f0655f82d93ed9be34966f32413464520a6976 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Oct 2023 15:47:29 +0000 Subject: [PATCH 05/10] Bump jaxb-runtime from 4.0.2 to 4.0.3 (#334) Bumps jaxb-runtime from 4.0.2 to 4.0.3. --- updated-dependencies: - dependency-name: org.glassfish.jaxb:jaxb-runtime dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Brian Kroth --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b2b486d36..af91dd96e 100644 --- a/pom.xml +++ b/pom.xml @@ -225,7 +225,7 @@ org.glassfish.jaxb jaxb-runtime - 4.0.2 + 4.0.3 From 74b5ba740ecf1b0d759a71e33dbdf8abf431f197 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Oct 2023 12:15:42 -0500 Subject: [PATCH 06/10] Bump org.codehaus.janino:janino from 3.1.9 to 3.1.10 (#371) Bumps [org.codehaus.janino:janino](https://github.com/janino-compiler/janino) from 3.1.9 to 3.1.10. - [Release notes](https://github.com/janino-compiler/janino/releases) - [Commits](https://github.com/janino-compiler/janino/compare/v3.1.9...v3.1.10) --- updated-dependencies: - dependency-name: org.codehaus.janino:janino dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index af91dd96e..6a0f407e7 100644 --- a/pom.xml +++ b/pom.xml @@ -251,7 +251,7 @@ org.codehaus.janino janino - 3.1.9 + 3.1.10 From d14c30061a0bc3ecc5c8b26e1aedf2ce3ca31445 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Oct 2023 17:26:57 +0000 Subject: [PATCH 07/10] Bump maven-release-plugin from 3.0.0 to 3.0.1 (#326) Bumps [maven-release-plugin](https://github.com/apache/maven-release) from 3.0.0 to 3.0.1. - [Release notes](https://github.com/apache/maven-release/releases) - [Commits](https://github.com/apache/maven-release/compare/maven-release-3.0.0...maven-release-3.0.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-release-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6a0f407e7..cf2d39cb3 100644 --- a/pom.xml +++ b/pom.xml @@ -321,7 +321,7 @@ maven-release-plugin - 3.0.0 + 3.0.1 false v@{version} From e25b8ad3110c5591f96c433843868e064a5a41a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Oct 2023 11:16:02 -0500 Subject: [PATCH 08/10] Bump org.immutables:value from 2.9.0 to 2.10.0 (#374) Bumps [org.immutables:value](https://github.com/immutables/immutables) from 2.9.0 to 2.10.0. - [Release notes](https://github.com/immutables/immutables/releases) - [Commits](https://github.com/immutables/immutables/commits/2.10.0) --- updated-dependencies: - dependency-name: org.immutables:value dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cf2d39cb3..430129141 100644 --- a/pom.xml +++ b/pom.xml @@ -244,7 +244,7 @@ org.immutables value - 2.9.0 + 2.10.0 provided From 546afa60dae4f8a6b00b84b77c77ff7684e494ad Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Oct 2023 16:26:56 +0000 Subject: [PATCH 09/10] Bump org.apache.commons:commons-lang3 from 3.12.0 to 3.13.0 (#373) Bumps org.apache.commons:commons-lang3 from 3.12.0 to 3.13.0. --- updated-dependencies: - dependency-name: org.apache.commons:commons-lang3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Brian Kroth --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 430129141..f33d51b32 100644 --- a/pom.xml +++ b/pom.xml @@ -183,7 +183,7 @@ org.apache.commons commons-lang3 - 3.12.0 + 3.13.0 From 8c86bd461904d46f4c9707e0f88c1b93854504ed Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Nov 2023 13:49:13 -0600 Subject: [PATCH 10/10] Bump org.apache.maven.plugins:maven-clean-plugin from 3.2.0 to 3.3.2 (#384) Bumps [org.apache.maven.plugins:maven-clean-plugin](https://github.com/apache/maven-clean-plugin) from 3.2.0 to 3.3.2. - [Release notes](https://github.com/apache/maven-clean-plugin/releases) - [Commits](https://github.com/apache/maven-clean-plugin/compare/maven-clean-plugin-3.2.0...maven-clean-plugin-3.3.2) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-clean-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f33d51b32..f2392933d 100644 --- a/pom.xml +++ b/pom.xml @@ -306,7 +306,7 @@ maven-clean-plugin - 3.2.0 + 3.3.2