From e385c254b0261f62c9befe4735fd1e555351e0e6 Mon Sep 17 00:00:00 2001 From: Zhichun Wu Date: Tue, 23 Feb 2021 22:17:42 +0800 Subject: [PATCH] Add benchmark --- .github/workflows/benchmark.yml | 124 ++++++++++++ .gitignore | 1 + clickhouse-benchmark/docs/results/v0.2.4.json | 0 clickhouse-benchmark/docs/results/v0.2.5.json | 0 clickhouse-benchmark/docs/results/v0.2.6.json | 0 clickhouse-benchmark/pom.xml | 189 ++++++++++++++++++ .../java/tech/clickhouse/benchmark/Basic.java | 34 ++++ .../clickhouse/benchmark/ClientState.java | 61 ++++++ .../tech/clickhouse/benchmark/Constants.java | 18 ++ .../tech/clickhouse/benchmark/Insertion.java | 76 +++++++ .../clickhouse/benchmark/JdbcBenchmark.java | 119 +++++++++++ .../tech/clickhouse/benchmark/JdbcDriver.java | 69 +++++++ .../java/tech/clickhouse/benchmark/Query.java | 78 ++++++++ .../clickhouse/benchmark/ServerState.java | 117 +++++++++++ 14 files changed, 886 insertions(+) create mode 100644 .github/workflows/benchmark.yml create mode 100644 clickhouse-benchmark/docs/results/v0.2.4.json create mode 100644 clickhouse-benchmark/docs/results/v0.2.5.json create mode 100644 clickhouse-benchmark/docs/results/v0.2.6.json create mode 100644 clickhouse-benchmark/pom.xml create mode 100644 clickhouse-benchmark/src/main/java/tech/clickhouse/benchmark/Basic.java create mode 100644 clickhouse-benchmark/src/main/java/tech/clickhouse/benchmark/ClientState.java create mode 100644 clickhouse-benchmark/src/main/java/tech/clickhouse/benchmark/Constants.java create mode 100644 clickhouse-benchmark/src/main/java/tech/clickhouse/benchmark/Insertion.java create mode 100644 clickhouse-benchmark/src/main/java/tech/clickhouse/benchmark/JdbcBenchmark.java create mode 100644 clickhouse-benchmark/src/main/java/tech/clickhouse/benchmark/JdbcDriver.java create mode 100644 clickhouse-benchmark/src/main/java/tech/clickhouse/benchmark/Query.java create mode 100644 clickhouse-benchmark/src/main/java/tech/clickhouse/benchmark/ServerState.java diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml new file mode 100644 index 000000000..aa3455c0a --- /dev/null +++ b/.github/workflows/benchmark.yml @@ -0,0 +1,124 @@ +name: Benchmark + +on: + workflow_dispatch: + inputs: + clickhouse: + description: "ClickHouse version" + required: true + default: "latest" + java: + description: "Java version" + required: true + default: "8" + baseline: + description: "Baseline to compare" + required: true + default: "0.2.6" + driver: + description: "Driver version" + required: true + default: "0.3.0-SNAPSHOT" + options: + description: "Benchmark options" + required: true + default: "-prof gc 'Query|Insertion'" + pr: + description: "Pull request#" + required: false + +jobs: + benchmark: + runs-on: ubuntu-latest + name: Benchmark on demand + if: github.event_name == 'workflow_dispatch' + steps: + - name: Check out repository + uses: actions/checkout@v2 + - name: Check out PR + run: | + git fetch --no-tags --prune --progress --no-recurse-submodules --depth=1 \ + origin pull/${{ github.event.inputs.pr }}/merge:merged-pr && git checkout merged-pr + if: github.event.inputs.pr != '' + - name: Set up JDK ${{ github.event.inputs.java }} + uses: actions/setup-java@v1 + with: + java-version: ${{ github.event.inputs.java }} + continue-on-error: true + - name: Update version and group id + run: | + find . -type f -name "pom.xml" -exec sed -i -e 's|${revision}|${{ github.event.inputs.driver }}|g' \ + -e 's|${parent.groupId}|tech.clickhouse|g' '{}' \; + sed -i -e 's|^\( \).*\(\)$|\1${{ github.event.inputs.driver }}\2|' pom.xml + continue-on-error: true + - name: Install driver as needed + run: mvn --batch-mode --update-snapshots -q -DskipTests install + if: endsWith(github.event.inputs.driver, '-SNAPSHOT') + continue-on-error: true + - name: Build project + run: | + cd clickhouse-benchmark + mvn --batch-mode --update-snapshots -Drevision=${{ github.event.inputs.driver }} \ + -DclickhouseVersion=${{ github.event.inputs.clickhouse }} install + java -jar target/benchmarks.jar -rf json ${{ github.event.inputs.options }} > output.txt + echo "BENCHMARK_REPORT<> $GITHUB_ENV + tail -n +$(grep -n '^REMEMBER:' output.txt | tail -1 | awk -F: '{print $1+6}') output.txt | head -n -2 | grep -v ':ยท' >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + cd - + id: benchmark + continue-on-error: true + - name: Record benchmark results + run: | + mv -fv clickhouse-benchmark/jmh-result.json \ + clickhouse-benchmark/docs/results/v${{ github.event.inputs.driver }}.json + if: github.event.inputs.pr == '' + continue-on-error: true + - name: Record pull request benchmark results + run: | + mv -fv clickhouse-benchmark/jmh-result.json \ + clickhouse-benchmark/docs/results/pull-request_${{github.event.inputs.pr}}.json + if: github.event.inputs.pr != '' + continue-on-error: true + - name: Commit benchmark result + uses: zhicwu/add-and-commit@v7 + with: + add: 'clickhouse-benchmark/docs/results/*.json' + author_name: zhicwu + author_email: 4270380+zhicwu@users.noreply.github.com + branch: develop + message: 'Record benchmark results' + push: true + continue-on-error: true + - name: Get commit hash + run: | + echo ::set-output name=hash::$(git log --pretty=format:'%H' -n 1) + id: commit + continue-on-error: true + - name: 'Comment PR' + uses: actions/github-script@v3 + if: github.event.inputs.pr != '' + env: + PR_NO: ${{ github.event.inputs.pr }} + COMMIT_HASH: ${{ steps.commit.outputs.hash }} + COMPARE_TO: ${{ github.event.inputs.baseline }} + CLICKHOUSE_VRESION: ${{ github.event.inputs.clickhouse }} + DRIVER_VRESION: ${{ github.event.inputs.driver }} + JAVA_VERSION: ${{ github.event.inputs.java }} + PREV_STEP_RESULT: '${{ steps.benchmark.outcome }}' + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const issue_number = process.env.PR_NO; + const { repo: { owner, repo } } = context; + const result = process.env.PREV_STEP_RESULT; + const compareUrl = `http://jmh.morethan.io/?sources=https://raw.githubusercontent.com/${owner}/${repo}/develop/clickhouse-benchmark/docs/results/v${process.env.COMPARE_TO}.json,https://raw.githubusercontent.com/${owner}/${repo}/${process.env.COMMIT_HASH || 'develop'}/clickhouse-benchmark/docs/results/${issue_number ? 'pull-request_' + issue_number : 'v' + process.env.DRIVER_VERSION}.json`; + // const benchmarkUrl = `http://jmh.morethan.io/?source=https://raw.githubusercontent.com/${owner}/${repo}/${process.env.COMMIT_HASH || 'develop'}/clickhouse-benchmark/docs/results/${issue_number ? 'pull-request_' + issue_number : 'v' + process.env.DRIVER_VERSION}.json`; + const buildUrl = `https://github.com/${owner}/${repo}/actions/runs/${context.runId}`; + const flag = result === 'success' + ? ':green_circle:' + : (result === 'failure' ? ':red_circle:' : ':yellow_circle:'); + const msg = `${flag} [benchmark](${compareUrl}) using JDK [${process.env.JAVA_VERSION}] and ClickHouse [${process.env.CLICKHOUSE_VRESION}]: [${result}](${buildUrl})` + + '
\nExpand to see details...\n\n```\n' + + process.env.BENCHMARK_REPORT || '' + + '\n```\n
'; + github.issues.createComment({ issue_number, owner, repo, body: msg }); diff --git a/.gitignore b/.gitignore index a3764496c..285aa2d41 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ # VSCode .vscode +.factorypath # Eclipse .classpath diff --git a/clickhouse-benchmark/docs/results/v0.2.4.json b/clickhouse-benchmark/docs/results/v0.2.4.json new file mode 100644 index 000000000..e69de29bb diff --git a/clickhouse-benchmark/docs/results/v0.2.5.json b/clickhouse-benchmark/docs/results/v0.2.5.json new file mode 100644 index 000000000..e69de29bb diff --git a/clickhouse-benchmark/docs/results/v0.2.6.json b/clickhouse-benchmark/docs/results/v0.2.6.json new file mode 100644 index 000000000..e69de29bb diff --git a/clickhouse-benchmark/pom.xml b/clickhouse-benchmark/pom.xml new file mode 100644 index 000000000..c967c9eb6 --- /dev/null +++ b/clickhouse-benchmark/pom.xml @@ -0,0 +1,189 @@ + + 4.0.0 + + tech.clickhouse + clickhouse-benchmark + ${revision} + jar + + clickhouse-benchmark + + + 0.3.0-SNAPSHOT + 1.4.4 + 2.5.3 + 2.7.2 + 8.0.23 + 2.5.3 + 1.15.2 + UTF-8 + 1.27 + 1.8 + benchmarks + + + + + org.openjdk.jmh + jmh-core + ${jmh.version} + + + org.openjdk.jmh + jmh-generator-annprocess + ${jmh.version} + provided + + + + + ru.yandex.clickhouse + clickhouse-jdbc + ${revision} + shaded + + + * + * + + + + + cc.blynk.clickhouse + clickhouse4j + ${clickhouse4j-driver.version} + + + * + * + + + + + org.mariadb.jdbc + mariadb-java-client + ${mariadb-driver.version} + + + * + * + + + + + mysql + mysql-connector-java + ${mysql-driver.version} + + + * + * + + + + + com.github.housepower + clickhouse-native-jdbc-shaded + ${native-driver.version} + + + * + * + + + + + org.testcontainers + testcontainers + ${testcontainers.version} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.0 + + ${javac.target} + ${javac.target} + ${javac.target} + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.1 + + + package + + shade + + + ${shade.name} + + + org.openjdk.jmh.Main + + + + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + + + + + + maven-clean-plugin + 2.5 + + + maven-deploy-plugin + 2.8.1 + + + maven-install-plugin + 2.5.1 + + + maven-jar-plugin + 2.4 + + + maven-javadoc-plugin + 2.9.1 + + + maven-resources-plugin + 2.6 + + + maven-site-plugin + 3.3 + + + maven-source-plugin + 2.2.1 + + + maven-surefire-plugin + 2.17 + + + + + diff --git a/clickhouse-benchmark/src/main/java/tech/clickhouse/benchmark/Basic.java b/clickhouse-benchmark/src/main/java/tech/clickhouse/benchmark/Basic.java new file mode 100644 index 000000000..da543ee77 --- /dev/null +++ b/clickhouse-benchmark/src/main/java/tech/clickhouse/benchmark/Basic.java @@ -0,0 +1,34 @@ +package tech.clickhouse.benchmark; + +import java.sql.ResultSet; +import java.sql.Statement; +import java.util.Collections; + +import org.openjdk.jmh.annotations.Benchmark; + +public class Basic extends JdbcBenchmark { + @Benchmark + public int selectOneRandomNumber(ClientState state) throws Throwable { + final int num = (int) (Math.random() * 1000); + + try (Statement stmt = executeQuery(state, "select ? as n", num)) { + ResultSet rs = stmt.getResultSet(); + + rs.next(); + + if (num != rs.getInt(1)) { + throw new IllegalStateException(); + } + + return num; + } + } + + @Benchmark + public int insertOneRandomNumber(ClientState state) throws Throwable { + final int num = (int) (Math.random() * 1000); + + return executeInsert(state, "insert into test_insert(i) values(?)", + Collections.enumeration(Collections.singletonList(new Object[] { num }))); + } +} diff --git a/clickhouse-benchmark/src/main/java/tech/clickhouse/benchmark/ClientState.java b/clickhouse-benchmark/src/main/java/tech/clickhouse/benchmark/ClientState.java new file mode 100644 index 000000000..cc22324e7 --- /dev/null +++ b/clickhouse-benchmark/src/main/java/tech/clickhouse/benchmark/ClientState.java @@ -0,0 +1,61 @@ +package tech.clickhouse.benchmark; + +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Properties; + +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; + +@State(Scope.Thread) +public class ClientState { + @Param(value = { "clickhouse4j", Constants.CLICKHOUSE_DRIVER, "clickhouse-native-jdbc-shaded", + "mariadb-java-client", "mysql-connector-java" }) + private String client; + + @Param(value = { Constants.NORMAL_STATEMENT, Constants.PREPARED_STATEMENT }) + private String statement; + + private Connection conn; + + @Setup(Level.Trial) + public void doSetup(ServerState serverState) throws Exception { + JdbcDriver driver = JdbcDriver.from(client); + + try { + conn = ((java.sql.Driver) Class.forName(driver.getClassName()).getDeclaredConstructor().newInstance()) + .connect(String.format(driver.getUrlTemplate(), serverState.getHost(), + serverState.getPort(driver.getDefaultPort()), serverState.getDatabase(), + serverState.getUser(), serverState.getPassword()), new Properties()); + + try (Statement s = conn.createStatement()) { + s.execute( + "create table if not exists test_insert(i Nullable(UInt64), s Nullable(String), t Nullable(DateTime))engine=Memory"); + } + } catch (SQLException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } + + @TearDown(Level.Trial) + public void doTearDown() throws SQLException { + try (Statement s = conn.createStatement()) { + s.execute("drop table if exists test_insert"); + } + conn.close(); + } + + public Connection getConnection() { + return this.conn; + } + + public boolean usePreparedStatement() { + return Constants.PREPARED_STATEMENT.equals(this.statement); + } +} diff --git a/clickhouse-benchmark/src/main/java/tech/clickhouse/benchmark/Constants.java b/clickhouse-benchmark/src/main/java/tech/clickhouse/benchmark/Constants.java new file mode 100644 index 000000000..e2873579b --- /dev/null +++ b/clickhouse-benchmark/src/main/java/tech/clickhouse/benchmark/Constants.java @@ -0,0 +1,18 @@ +package tech.clickhouse.benchmark; + +public interface Constants { + public static final String CLICKHOUSE_DRIVER = "clickhouse-jdbc"; + + public static final String DEFAULT_HOST = "127.0.0.1"; + public static final String DEFAULT_DB = "system"; + public static final String DEFAULT_USER = "default"; + public static final String DEFAULT_PASSWD = ""; + + public static final int GRPC_PORT = 9100; + public static final int HTTP_PORT = 8123; + public static final int MYSQL_PORT = 3307; + public static final int NATIVE_PORT = 9000; + + public static final String NORMAL_STATEMENT = "normal"; + public static final String PREPARED_STATEMENT = "prepared"; +} diff --git a/clickhouse-benchmark/src/main/java/tech/clickhouse/benchmark/Insertion.java b/clickhouse-benchmark/src/main/java/tech/clickhouse/benchmark/Insertion.java new file mode 100644 index 000000000..15fa43f49 --- /dev/null +++ b/clickhouse-benchmark/src/main/java/tech/clickhouse/benchmark/Insertion.java @@ -0,0 +1,76 @@ +package tech.clickhouse.benchmark; + +import java.sql.Timestamp; +// import java.util.Collections; +import java.util.Enumeration; + +import org.openjdk.jmh.annotations.Benchmark; + +public class Insertion extends JdbcBenchmark { + // @Benchmark + // public int insertOneNumber(ClientState state) throws Throwable { + // return executeInsert(state, "insert into test_insert(i) values(?)", + // Collections.enumeration(Collections.singletonList(new Object[] { (int) + // (Math.random() * 1000) }))); + // } + + @Benchmark + public int insert10kUInt64Rows(ClientState state) throws Throwable { + final int rows = 10000; + final int num = (int) (Math.random() * rows); + + return executeInsert(state, "insert into test_insert(i) values(?)", new Enumeration() { + int counter = 0; + + @Override + public boolean hasMoreElements() { + return counter < rows; + } + + @Override + public Object[] nextElement() { + return new Object[] { num + (counter++) }; + } + }); + } + + @Benchmark + public int insert10kStringRows(ClientState state) throws Throwable { + final int rows = 10000; + final int num = (int) (Math.random() * rows); + + return executeInsert(state, "insert into test_insert(s) values(?)", new Enumeration() { + int counter = 0; + + @Override + public boolean hasMoreElements() { + return counter < rows; + } + + @Override + public Object[] nextElement() { + return new Object[] { String.valueOf(num + (counter++)) }; + } + }); + } + + @Benchmark + public int insert10kTimestampRows(ClientState state) throws Throwable { + final int rows = 10000; + final int num = (int) (Math.random() * rows); + + return executeInsert(state, "insert into test_insert(t) values(?)", new Enumeration() { + int counter = 0; + + @Override + public boolean hasMoreElements() { + return counter < rows; + } + + @Override + public Object[] nextElement() { + return new Object[] { new Timestamp(num + (counter++)) }; + } + }); + } +} diff --git a/clickhouse-benchmark/src/main/java/tech/clickhouse/benchmark/JdbcBenchmark.java b/clickhouse-benchmark/src/main/java/tech/clickhouse/benchmark/JdbcBenchmark.java new file mode 100644 index 000000000..e1e4afdc3 --- /dev/null +++ b/clickhouse-benchmark/src/main/java/tech/clickhouse/benchmark/JdbcBenchmark.java @@ -0,0 +1,119 @@ +package tech.clickhouse.benchmark; + +import org.openjdk.jmh.annotations.*; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Enumeration; +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +@State(Scope.Benchmark) +@Warmup(iterations = 10, timeUnit = TimeUnit.SECONDS, time = 1) +@Measurement(iterations = 10, timeUnit = TimeUnit.SECONDS, time = 1) +@Fork(value = 2) +@Threads(value = -1) +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.SECONDS) +public abstract class JdbcBenchmark { + // batch size for mutation + private final int batchSize = Integer.parseInt(System.getProperty("batchSize", "1000")); + // fetch size for query + private final int fetchSize = Integer.parseInt(System.getProperty("fetchSize", "1000")); + + protected PreparedStatement setParameters(PreparedStatement s, Object... values) throws SQLException { + if (values != null && values.length > 0) { + int index = 1; + for (Object v : values) { + s.setObject(index++, v); + } + } + + return s; + } + + protected String replaceParameters(String sql, Object... values) { + if (values != null && values.length > 0) { + for (Object v : values) { + int index = sql.indexOf('?'); + if (index == -1) { + break; + } + + String expr = null; + if (v instanceof Number) { + expr = String.valueOf(v); + } else { + expr = "'" + v + "'"; // without escaping... + } + + sql = sql.substring(0, index) + expr + sql.substring(index + 1); + } + } + + return sql; + } + + private int processBatch(Statement s, String sql, Enumeration generator) throws SQLException { + int rows = 0; + int counter = 0; + PreparedStatement ps = s instanceof PreparedStatement ? (PreparedStatement) s : null; + while (generator.hasMoreElements()) { + Object[] values = generator.nextElement(); + if (ps != null) { + setParameters(ps, values).addBatch(); + } else { + s.addBatch(replaceParameters(sql, values)); + } + if (++counter % batchSize == 0) { + rows += s.executeBatch().length; + } + } + + if (counter % batchSize != 0) { + rows += s.executeBatch().length; + } + + return rows; + } + + protected int executeInsert(ClientState state, String sql, Enumeration generator) throws SQLException { + Objects.requireNonNull(generator); + + final Connection conn = state.getConnection(); + int rows = 0; + + if (state.usePreparedStatement()) { + try (PreparedStatement s = conn.prepareStatement(sql)) { + rows = processBatch(s, sql, generator); + } + } else { + try (Statement s = conn.createStatement()) { + rows = processBatch(s, sql, generator); + } + } + + return rows; + } + + protected Statement executeQuery(ClientState state, String sql, Object... values) throws SQLException { + final Statement stmt; + + final Connection conn = state.getConnection(); + + if (state.usePreparedStatement()) { + PreparedStatement s = conn.prepareStatement(sql); + s.setFetchSize(fetchSize); + setParameters(s, values).executeQuery(); + stmt = s; + } else { + stmt = conn.createStatement(); + stmt.setFetchSize(fetchSize); + stmt.executeQuery(replaceParameters(sql, values)); + } + + return stmt; + } +} diff --git a/clickhouse-benchmark/src/main/java/tech/clickhouse/benchmark/JdbcDriver.java b/clickhouse-benchmark/src/main/java/tech/clickhouse/benchmark/JdbcDriver.java new file mode 100644 index 000000000..5476eb9cd --- /dev/null +++ b/clickhouse-benchmark/src/main/java/tech/clickhouse/benchmark/JdbcDriver.java @@ -0,0 +1,69 @@ +package tech.clickhouse.benchmark; + +public enum JdbcDriver { + // ClickHouse4j + Clickhouse4j("cc.blynk.clickhouse.ClickHouseDriver", + "jdbc:clickhouse://%s:%s/%s?ssl=false&user=%s&password=%s&use_server_time_zone=false&use_time_zone=UTC", + Constants.HTTP_PORT), + // ClickHouse JDBC Driver + ClickhouseJdbc("ru.yandex.clickhouse.ClickHouseDriver", + "jdbc:clickhouse://%s:%s/%s?ssl=false&user=%s&password=%s&use_server_time_zone=false&use_time_zone=UTC", + Constants.HTTP_PORT), + // ClickHouse Native JDBC Driver + ClickhouseNativeJdbcShaded("com.github.housepower.jdbc.ClickHouseDriver", + "jdbc:clickhouse://%s:%s/%s?ssl=false&user=%s&password=%s&use_server_time_zone=false&use_time_zone=UTC", + Constants.NATIVE_PORT), + + // MariaDB Java Client + MariadbJavaClient("org.mariadb.jdbc.Driver", + "jdbc:mariadb://%s:%s/%s?user=%s&password=%s&useSSL=false&useCompression=true&useServerPrepStmts=false&rewriteBatchedStatements=true&cachePrepStmts=true&serverTimezone=UTC", + Constants.MYSQL_PORT), + + // MySQL Connector/J + MysqlConnectorJava("com.mysql.cj.jdbc.Driver", + "jdbc:mysql://%s:%s/%s?user=%s&password=%s&useSSL=false&useCompression=true&useServerPrepStmts=false&rewriteBatchedStatements=true&cachePrepStmts=true&connectionTimeZone=UTC", + Constants.MYSQL_PORT); + + private final String className; + private final String urlTemplate; + private final int defaultPort; + + public static JdbcDriver from(String driver) { + if (driver == null || driver.isEmpty()) { + throw new IllegalArgumentException("Non-empty driver is needed"); + } + + String[] parts = driver.split(" "); + if (parts.length > 2) { + throw new IllegalArgumentException("Only format ' [version]' is supported!"); + } + + String name = parts[0].replace("-", ""); + + for (JdbcDriver d : JdbcDriver.values()) { + if (d.name().equalsIgnoreCase(name)) { + return d; + } + } + + throw new IllegalArgumentException("Unsupported driver: " + name); + } + + JdbcDriver(String className, String urlTemplate, int defaultPort) { + this.className = className; + this.urlTemplate = urlTemplate; + this.defaultPort = defaultPort; + } + + public String getClassName() { + return this.className; + } + + public String getUrlTemplate() { + return this.urlTemplate; + } + + public int getDefaultPort() { + return this.defaultPort; + } +} diff --git a/clickhouse-benchmark/src/main/java/tech/clickhouse/benchmark/Query.java b/clickhouse-benchmark/src/main/java/tech/clickhouse/benchmark/Query.java new file mode 100644 index 000000000..174b2943e --- /dev/null +++ b/clickhouse-benchmark/src/main/java/tech/clickhouse/benchmark/Query.java @@ -0,0 +1,78 @@ +package tech.clickhouse.benchmark; + +import java.sql.ResultSet; +import java.sql.Statement; +import java.sql.Timestamp; + +import org.openjdk.jmh.annotations.Benchmark; + +public class Query extends JdbcBenchmark { + @Benchmark + public int select10kUInt64Rows(ClientState state) throws Throwable { + int rows = 10000; + int num = (int) (Math.random() * rows); + try (Statement stmt = executeQuery(state, "select * from system.numbers where number > ? limit " + rows, num)) { + ResultSet rs = stmt.getResultSet(); + + float avg = 0.0F; + int count = 0; + while (rs.next()) { + avg = (rs.getInt(1) + avg * count) / (++count); + } + + if (count != rows) { + throw new IllegalStateException(); + } + + return count; + } + } + + @Benchmark + public int select10kStringRows(ClientState state) throws Throwable { + int rows = 10000; + int num = (int) (Math.random() * rows); + try (Statement stmt = executeQuery(state, + "select toString(number) as s from system.numbers where number > ? limit " + rows, num)) { + ResultSet rs = stmt.getResultSet(); + + int count = 0; + String str = null; + while (rs.next()) { + str = rs.getString(1); + count++; + } + + if (count != rows) { + throw new IllegalStateException(); + } + + return count; + } + } + + @Benchmark + public int select10kTimestampRows(ClientState state) throws Throwable { + int rows = 10000; + int num = (int) (Math.random() * rows); + try (Statement stmt = executeQuery(state, + "select toDateTime('2021-02-20 13:15:20') + number as d from system.numbers where number > ? limit " + + rows, + num)) { + ResultSet rs = stmt.getResultSet(); + + int count = 0; + Timestamp ts = null; + while (rs.next()) { + ts = rs.getTimestamp(1); + count++; + } + + if (count != rows) { + throw new IllegalStateException(); + } + + return count; + } + } +} diff --git a/clickhouse-benchmark/src/main/java/tech/clickhouse/benchmark/ServerState.java b/clickhouse-benchmark/src/main/java/tech/clickhouse/benchmark/ServerState.java new file mode 100644 index 000000000..71c2ab9c4 --- /dev/null +++ b/clickhouse-benchmark/src/main/java/tech/clickhouse/benchmark/ServerState.java @@ -0,0 +1,117 @@ +package tech.clickhouse.benchmark; + +import java.net.Inet4Address; +import java.net.InetAddress; +import java.net.InterfaceAddress; +import java.net.NetworkInterface; +import java.time.Duration; +import java.util.Enumeration; + +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.images.builder.ImageFromDockerfile; + +import static java.time.temporal.ChronoUnit.SECONDS; + +@State(Scope.Benchmark) +public class ServerState { + static String getLocalIpAddress() { + String localIpAddress = null; + + try { + for (Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); interfaces + .hasMoreElements();) { + NetworkInterface i = interfaces.nextElement(); + if (i.isUp() && !i.isLoopback() && !i.isPointToPoint() && !i.isVirtual()) { + for (InterfaceAddress addr : i.getInterfaceAddresses()) { + InetAddress inetAddr = addr.getAddress(); + + if (!(inetAddr instanceof Inet4Address)) { + continue; + } + + localIpAddress = inetAddr.getHostAddress(); + break; + } + } + + if (localIpAddress != null) { + break; + } + } + } catch (Exception e) { + // ignore exception + } + + return localIpAddress != null ? localIpAddress : Constants.DEFAULT_HOST; + } + + private final String host = System.getProperty("dbHost", Constants.DEFAULT_HOST); + private final String user = System.getProperty("dbUser", Constants.DEFAULT_USER); + private final String passwd = System.getProperty("dbPasswd", Constants.DEFAULT_PASSWD); + private final String db = System.getProperty("dbName", Constants.DEFAULT_DB); + + private final String localIpAddress = getLocalIpAddress(); + + private GenericContainer container = null; + + @Setup(Level.Trial) + public void doSetup() throws Exception { + if (System.getProperty("dbHost") != null) { + return; + } + + String imageTag = System.getProperty("clickhouseVersion"); + + if (imageTag == null || (imageTag = imageTag.trim()).isEmpty()) { + imageTag = ""; + } else { + imageTag = ":" + imageTag; + } + + final String imageNameWithTag = "yandex/clickhouse-server" + imageTag; + + container = new GenericContainer<>(new ImageFromDockerfile().withDockerfileFromBuilder(builder -> builder + .from(imageNameWithTag) + .run("echo '0.0.0.091003307' > /etc/clickhouse-server/config.d/custom.xml"))) + .withExposedPorts(Constants.GRPC_PORT, Constants.HTTP_PORT, Constants.MYSQL_PORT, + + Constants.NATIVE_PORT) + .waitingFor(Wait.forHttp("/ping").forPort(Constants.HTTP_PORT).forStatusCode(200) + .withStartupTimeout(Duration.of(60, SECONDS))); + + container.start(); + } + + @TearDown(Level.Trial) + public void doTearDown() throws Exception { + if (container != null) { + container.stop(); + } + } + + public String getHost() { + return container != null ? localIpAddress : host; + } + + public int getPort(int defaultPort) { + return container != null ? container.getMappedPort(defaultPort) : defaultPort; + } + + public String getUser() { + return container != null ? Constants.DEFAULT_USER : user; + } + + public String getPassword() { + return container != null ? Constants.DEFAULT_PASSWD : passwd; + } + + public String getDatabase() { + return container != null ? Constants.DEFAULT_DB : db; + } +}