Performance Testing the H2 Database¶
Apache JMeter for H2 profiling¶
Apache JMeter is a powerful tool for profiling any database through a JDBC connection (https://jmeter.apache.org/download_jmeter.cgi). By default, JMeter does not contain any H2 driver. A H2 JAR should be added to apache-jmeter-*/lib/
in order to be able to use an org.h2.Driver
JDBC Driver class. Also recommended is to increase the Java memory limit by editing apache-jmeter-*/bin/jmeter
. For the examples below, the 5.3 version was used.
File > Templates... > select JDBC Load Test > Create
). This will generate a test plan containing:
- JDBC Connection Configuration: used for defining the connection parameters
- Set theAuto Commit
andTransaction Isolation
to the reflect testcase's approach
- Set theDatabase URL
to the connection string used by FWD (example:jdbc:h2:mem:_temp;db_close_delay=-1;mv_store=false;query_cache_size=1024;
). In order to change to the MVStore engine, set themv_store
flag to true. For using private databases for each user, omit the name of the database (example:jdbc:h2:mem:;
). For using a not in-memory database, usefile
instead ofmem
(example:jdbc:h2:file:~/.h2/fwd.h2;
). Other connection string options can be found in the official H2 documentation.
- SetJDBC Driver class
toorg.h2.Driver
- SetUsername
tosa
and let thePassword
empty - Thread Group: used for defining the workload for each user.
- SetNumber of Threads (users)
to the number of users which should test the database concurrently.
- SetLoop count
to the number of times the provided test should be executed sequentially by each user. - JDBC Request: used for encapsulating the SQL which is meant to be executed by the database.
- SetQuery Type
to the appropriate statement type (Update Statement
is the most permissive as it allows commits and selects as well). - View Results Tree: displays the results for each JDBC request.
- Check the Sampler results periodically to ensue that JDBC requests do not fail.
After configuring the test plan accordingly, press the run button to trigger the testing. In the top-right corner, there is information about the time elapsed, JMeter related problems, the number of active threads and the total number of threads. A plan should be run multiple times - first run's time usually is far from the times of the next runs.
For comparing several H2 versions, the H2 JAR in apache-jmeter-*/lib/
should be changed. In order to load the replacing JAR, JMeter should be restarted. Note that sometimes the Database URL
should be changed as some options are added/removed from one version to another.
The testing results are based on the elapsed time which is displayed in the top-right corner.
Executing statements one-by-one (H2 v1.4.210 and newer versions)¶
The apache-jmeter-new-version-tests.zip contains several tests for profiling H2 through Apache JMeter. The archive contains:
1. A test plan (h2_profiling.jmx
file). This file contains several JDBC Connection Configurations and Thread Groups depending on the type of test that we want to execute:
- Each thread group represents a type of test (delete, insert, update, etc.) and have the following specifications:
- A single local temporary table with 10 fields namedtt
.
-full_test_5000
,heavy_commit_test_5000
,heavy_delete_test_5000
andheavy_update_test_5000
tests stress 10 users with 5 batches of 5000 statements.
-heavy_insert_test_5000
test stresses 5 users with 2 batches of 5000 statements.
-heavy_select_test_5000
test stresses 5 users with 5 batches of 5000 statements. - An additional thread group for testing the Hotel GUI application:
-hotel_test_6000
stresses 1 user with 250 batches of 6000 statements.
Note: the heavy
tests contain 50% statements of a specific type, while the full
test contains 20% statements of each type.
2. A generating script generate.py
derived from generate_full_sql.py
in apache-jmeter-tests.zip
used to randomly generate SQL tests. It was only modified to generate SQL statements for a larger table.
3. Another generating script sql_to_csv.py
used to parse SQL statements generated by the generate.py
script and add the Query Type
of the statement that will be executed by Apache JMeter. The statements are split into Select Statement (select) and Update Statement (update, delete, create, etc.).
Note: Output the results of generate.py
into a *.txt
file. Execute sql_to_csv.py [input_filename] [output_filename]
. The input should be the filename of the generated .txt file.
4. A few CSV files within a tests
folder than can be used directly for the current configured Thread Groups
. The CSV file for the hotel_test_6000
should prove to be really useful for future testing.
The test plans for the newer versions use a Loop Controller
and their structure is the following:
1. JDBC Connection Configuration: used for defining the connection parameters
h2_profiling.jmx
already contains 4 JDBC Connection Configurations:jdbc:h2:mem:;db_close_delay=-1;mv_store=false;query_cache_size=1024;
for testing version 1.4.200 in PageStore mode with private databases for each user.jdbc:h2:mem:;db_close_delay=-1;query_cache_size=1024;
for testing using MVStore (default mode) is any version with private databases.jdbc:h2:mem:_temp;db_close_delay=-1;mv_store=false;query_cache_size=1024;
for testing version 1.4.200 in PageStore mode with shared database.jdbc:h2:mem:_temp;db_close_delay=-1;query_cache_size=1024;
for testing using MVStore (default mode) in any version with shared database.
- each JDBC Connection Configuration can be enabled/disabled (only 1 connection should be enabled at once).
2. Thread Group: used for defining the workload for each user.
- Thread Groups can be enabled/disabled depending on the type of test that we want to do. (only 1 Thread Group should be enabled at once, running more tests may influence the results obtained).
- The Thread Group has the following structure:
- JDBC Request: used for encapsulating the SQL which is meant to be executed by the database.
- Used in most Thread Groups to create an initial table for running statements. Exception from this ishotel_test_6000
since statements were taken directly fromserver.log
.
- Loop Controller: used for running iterations infinitely or a number of times.
- CSV Data Set Config: used to set a
.csv
file for reading
- Set the filename to the path which contains the CSV created by thesql_to_csv.py
.
- Create 2 variable names:STMT,TYPE
.STMT
will represent the SQL statement andTYPE
will represent theQuery Type
for the SQL statement.
- Set the delimiter to&
. The delimiter is used to split a line and getSTMT
andTYPE
- Set the Recycle on EOF toTrue
. Once JMeter reaches the end of the file, it will go to the beginning and iterate the CSV file
- Stop thread on EOF toFalse
. Once a thread reaches the end of the file, it will not close.
- Sharing mode:Current thread
. Each thread will open and read the CSV file separately.
- The CSV Data Set Config reads line by line from the specified .csv file, splits the lines using the delimiter and sets the STMT and TYPE variables.
- JDBC Request:
- Set theQuery Type
to${TYPE}
and theQuery
contents to${STMT}
.
-STMT
andTYPE
will be taken from the CSV Data Set Config.
- View Results Tree: displays the results for each JDBC request.
- Uncheck the
Infinite
option and specify the number of statements in the CSV file (number of lines) unless Stop Thread on EOF is true. - CSV Data Set Config: used to set a
- JDBC Request: used for encapsulating the SQL which is meant to be executed by the database.
This profiling approach allows for clear results between 1.4.200 and newer versions. Old tests use a large string as Query
in a JDBC Request
while this method uses a CSV Data Set Config
that runs a single JDBC Request
per statement (line by line).
Executing batch statements (H2 v1.4.206 and previous versions)¶
A set of test plans was already created. The apache-jmeter-tests.zip contains several tests used by now when profiling H2 through Apache JMeter. The archive contains:- Saved test plans (
*.jmx
files). All of those contain a single local temporary table for each user namedtt
.
- Thefull_test_2000.jmx
test stresses 5 users with 5 batches of 2000 statements.
- The*_test_200.jmx
tests stress 200 users with 5 batches of 200 statements. The "heavy" tests contain 50% statements of a specific type, while the full tests contain 20% statements of each type. - A generating script
generate_full_sql.py
used for randomly generating SQL tests. This was used for creating the JDBC requests in the test plans above.
This kind of tests use a single JDBC request per user iteration. This may be seriously affected by latency, parsing, large string manipulation, lack of caching, etc. Use these only if you are going to test older versions of H2, as the newer ones (>1.4.200) do not even allow such configuration (especially if using prepared statements). Even so, it is more accurate to test FWD using the SQL statements "one-by-one". There is no use-case in FWD (at the moment of writing) where several statements (> 5) are batched, so this kind of tests are strictly speculative.
Also, the tests above contain randomly generated SQL and it doesn't necessarily reflect the use case in FWD. They are mainly used for plain performance tests in order to prove an increase or a decrease of performance from one version to another as an overall matter. They can also be used in order to test different behaviors by changing the connection string options, transaction isolation, auto commit or number of users.
H2 native profiling¶
Another way to profile H2 is to make use of the native profiling tool described at http://h2database.com/html/performance.html#database_profiling. This can tell how much time was spent on each statement, or the total time spent on a repetitive statement. Beside this, it does a counting job and accuracy computing. However, this is not a very strong tool after all as is does not provide a very accurate estimation of the times. In fact, it parses the trace file generated by the H2 database and interprets the log times, which finally can provide inaccurate information for certain cases. Another strong disadvantage is that the trace file seems to be generated only for databases which are not in-memory and that this type of profiling is for one user only.
The usage instructions for this type of profiling are already in depth described in the official documentation. For simple manual profiling, one can check the trace file in order to compute the time difference between the start and the finish. This can be more reliable than the "stetement-wise" profiling. For comparing several H2 versions, make sure that different databases are used. Otherwise, there will be only one trace file which will contain data from multiple runs.
Finally, a strong point is that this type of profiling can be easily used together with FWD, as the trace file is generated next to the disk persisted database (if the required changes are done in FWD in order to work which such database type) and it contains FWD specific generated statements.
Custom Java profiler¶
A way to profile H2 in regard with FWD is by using the fwd-h2-profiler.zip custom Java profiler. This contains:- Test.java: used for spawning several threads/users which will execute the same H2 test. It can run the same testing plan multiple times and compute the average, maximum time and minimum time.
- CustomTest.java: is an example of how a H2 stress test should look like.
- SqlTest.java: is a helper which reads a file and executes the SQL statements inside line by line.
log_to_sql.py
, which parses the FWD generatedserver.log
and extracts all SQL statements which are run against H2 (requires INFO logging level and H2 TRACE_LEVEL_FILE set on level 4). The file to be parsed should be in the same folder as the script and renamed toinput.txt
.log_to_java.py
, which parses the FWD generatedserver.log
and extracts all Java statements which use the H2 database (requires FINE logging level and H2 TRACE_LEVEL_FILE set on level 4). The file to be parsed should be in the same folder as the script and renamed toinput2.txt
.
Once the SQL statements are extracted, they can be used as separate testcase both for Apache JMeter and H2 native profiling. The custom Java profiler allows the testing of such SQL statements through SqlTest.java
. The disadvantage of using a plain SQL file is that there are some connection options which are not expressed. By using the generated Java statements, the exact behavior as in FWD is described (including connection settings). However, the parsed Java statements can't be included in a single Java file as it exceeds the maximum method size allowed. This means that the file should be either automatically parsed or manually reinterpreted.
This type of profiling is closer to the real FWD use case. The Test.java
class handles all the testing job by computing the times. Comparing H2 versions means including different H2 JAR in the class-path.
Running FWD-based performance tests for H2¶
There is a set of testcases designed to test performance of H2 database engine in FWD usage scenarios. That is performed by accessing (create/read/update/delete) a large number of records in the temp database. These testcases are a part of the Testcases repository, they are located in the h2_performance
subdirectory of this suite. You can use the following instructions to run these testcases:
1. Set up the tests using the instructions provided here Testcases. You don't need to run conversion step or any of the steps following it.
2. Put the names of all testcases in h2_performance
subdirectory into the conversion list file-cvt-list.txt
, which is:
h2_performance/perf-delete-bulk.p h2_performance/perf-update.p h2_performance/perf-create.p h2_performance/perf-update-foreach.p h2_performance/perf-buffer-copy.p h2_performance/perf-read.p h2_performance/perf-find-read.p h2_performance/perf-delete.p h2_performance/perf-read-foreach.p h2_performance/perf-test-runner.p
3. Convert these testcases and build jars:
ant deploy
4. Set h2_performance/perf-test-runner.p
as the entry procedure by modifying deploy/server/directory.xml
. Search for p2j-entry
and update this parameter:
<node class="string" name="p2j-entry"> <node-attribute name="value" value="com.goldencode.testcases.h2_performance.PerfTestRunner.execute"/> </node>
5. Start FWD server:
cd deploy/server ./server.sh
6. Run FWD client in a separate terminal:
cd ../client-gui ./client.sh
7. Wait for the client to finish. The results are in deploy/client-gui/perf-results.txt
© 2020 Golden Code Development Corporation. ALL RIGHTS RESERVED.