| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 92ca970 commit 80d8013
14 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -31,6 +31,7 @@ | |||
| 31 | 31 | <module>jdbc</module> | |
| 32 | 32 | <module>project-course</module> | |
| 33 | 33 | <module>slo</module> | |
| 34 | + <module>slo-workload</module> | ||
| 34 | 35 | </modules> | |
| 35 | 36 | ||
| 36 | 37 | <dependencyManagement> | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,144 @@ | |||
| 1 | + # YDB SLO Workload Tests | ||
| 2 | + | ||
| 3 | + This module hosts SLO (Service Level Objective) workloads that test the | ||
| 4 | + reliability of YDB Java clients under load and chaos using the | ||
| 5 | + [YDB SLO action](https://github.com/ydb-platform/ydb-slo-action). | ||
| 6 | + | ||
| 7 | + Each submodule is a self-contained, runnable workload that follows the same | ||
| 8 | + contract as the SDK SLO workload in [`../slo`](../slo): it reads its | ||
| 9 | + configuration from environment variables, runs setup/run/teardown phases, and | ||
| 10 | + pushes OpenTelemetry (OTLP) metrics that the action scrapes and compares | ||
| 11 | + between the current PR run and a baseline run. | ||
| 12 | + | ||
| 13 | + | Module | Component under test | Description | | ||
| 14 | + | --- | --- | --- | | ||
| 15 | + | [`jdbc`](jdbc) | `ydb-jdbc-driver` | Plain JDBC KV workload (no framework) | | ||
| 16 | + | ||
| 17 | + ## How a workload behaves | ||
| 18 | + | ||
| 19 | + Every workload runs three phases: | ||
| 20 | + | ||
| 21 | + 1. **Setup** — creates a partitioned KV table and prefills it with rows. | ||
| 22 | + 2. **Run** — drives concurrent read and write loops at fixed RPS for the | ||
| 23 | + configured duration. Each operation is timed and retried; the outcome is | ||
| 24 | + recorded as OTLP metrics. | ||
| 25 | + 3. **Teardown** — drops the workload table even if the run failed, so the | ||
| 26 | + cluster is left clean. | ||
| 27 | + | ||
| 28 | + While the workload runs, the SLO action injects chaos (node restarts, network | ||
| 29 | + black holes, container pauses). The metrics show how well the client copes. | ||
| 30 | + | ||
| 31 | + ## Metrics | ||
| 32 | + | ||
| 33 | + Every metric carries a `ref` label taken from the `WORKLOAD_REF` environment | ||
| 34 | + variable, which lets the report action separate the **current** run from the | ||
| 35 | + **baseline** run. Names are shown below in Prometheus form (dots become | ||
| 36 | + underscores during the OTLP → Prometheus conversion). | ||
| 37 | + | ||
| 38 | + | Metric | Type | Labels | | ||
| 39 | + | --- | --- | --- | | ||
| 40 | + | `sdk_operations_total` | counter | `operation_type`, `operation_status` | | ||
| 41 | + | `sdk_errors_total` | counter | `operation_type`, `error_kind` | | ||
| 42 | + | `sdk_retry_attempts_total` | counter | `operation_type`, `operation_status` | | ||
| 43 | + | `sdk_pending_operations` | up/down counter | `operation_type` | | ||
| 44 | + | `sdk_operation_latency_p50_seconds` | gauge | `operation_type`, `operation_status` (always `success`) | | ||
| 45 | + | `sdk_operation_latency_p95_seconds` | gauge | `operation_type`, `operation_status` (always `success`) | | ||
| 46 | + | `sdk_operation_latency_p99_seconds` | gauge | `operation_type`, `operation_status` (always `success`) | | ||
| 47 | + | ||
| 48 | + Latency percentiles are computed from per-operation HDR histograms and reflect | ||
| 49 | + only successful operations — failure latency is dominated by retry budgets and | ||
| 50 | + timeouts and would mask real regressions during chaos. Counters cover both | ||
| 51 | + branches, so availability is computed correctly. | ||
| 52 | + | ||
| 53 | + ## Inputs | ||
| 54 | + | ||
| 55 | + Connection details and run parameters come from environment variables: | ||
| 56 | + | ||
| 57 | + | Variable | Description | | ||
| 58 | + | --- | --- | | ||
| 59 | + | `YDB_JDBC_URL` | Full JDBC URL (`jdbc:ydb:...`), used verbatim if set | | ||
| 60 | + | `YDB_CONNECTION_STRING` | YDB connection string; prefixed with `jdbc:ydb:` | | ||
| 61 | + | `YDB_ENDPOINT` + `YDB_DATABASE` | Used to compose the connection string if the above are unset | | ||
| 62 | + | `YDB_TOKEN` | Optional auth token | | ||
| 63 | + | `WORKLOAD_REF` | Value of the `ref` label on every metric | | ||
| 64 | + | `WORKLOAD_NAME` | Workload name (also part of the table name) | | ||
| 65 | + | `WORKLOAD_DURATION` | Run duration in seconds | | ||
| 66 | + | `OTEL_EXPORTER_OTLP_ENDPOINT` | OTLP HTTP endpoint to push metrics to | | ||
| 67 | + | ||
| 68 | + KV tunables are passed on the command line and parsed by JCommander: | ||
| 69 | + | ||
| 70 | + ``` | ||
| 71 | + --read-rps <int> Target read RPS (default 1000) | ||
| 72 | + --write-rps <int> Target write RPS (default 100) | ||
| 73 | + --read-timeout-ms <int> Per-attempt read timeout in ms (default 10000) | ||
| 74 | + --write-timeout-ms <int> Per-attempt write timeout in ms (default 10000) | ||
| 75 | + --prefill-count <int> Rows to prefill before the run phase (default 1000) | ||
| 76 | + --partition-size <int> Auto-partitioning partition size in MB (default 1) | ||
| 77 | + --min-partition-count <int> Minimum number of table partitions (default 6) | ||
| 78 | + --max-partition-count <int> Maximum number of table partitions (default 1000) | ||
| 79 | + --duration <int> Override WORKLOAD_DURATION when > 0 | ||
| 80 | + ``` | ||
| 81 | + | ||
| 82 | + Unknown flags are ignored, so a workload accepts command strings designed for | ||
| 83 | + other SDKs without erroring. | ||
| 84 | + | ||
| 85 | + ## How CI uses this module | ||
| 86 | + | ||
| 87 | + This repository only hosts the workload sources. The CI that actually runs them | ||
| 88 | + lives in the repository of the component under test — for `jdbc` that is | ||
| 89 | + [`ydb-jdbc-driver`](https://github.com/ydb-platform/ydb-jdbc-driver) — mirroring | ||
| 90 | + how the SDK SLO workload in [`../slo`](../slo) is driven from the | ||
| 91 | + `ydb-java-sdk` repository rather than from here. | ||
| 92 | + | ||
| 93 | + The driver's workflow, via [`ydb-platform/ydb-slo-action`](https://github.com/ydb-platform/ydb-slo-action): | ||
| 94 | + | ||
| 95 | + 1. checks out the driver under test (current and baseline) and this repository | ||
| 96 | + for the workload sources; | ||
| 97 | + 2. `ydb-platform/ydb-slo-action/init` deploys a YDB cluster (storage + database | ||
| 98 | + nodes), Prometheus with an OTLP receiver, and a chaos monkey, exposing the | ||
| 99 | + database node IPs and the OTLP endpoint as step outputs; | ||
| 100 | + 3. builds the workload jar and runs it, pointing `YDB_CONNECTION_STRING` at a | ||
| 101 | + database node and `OTEL_EXPORTER_OTLP_ENDPOINT` at the Prometheus OTLP | ||
| 102 | + receiver; | ||
| 103 | + 4. `ydb-platform/ydb-slo-action/report` compares the current run against the | ||
| 104 | + baseline and posts a summary to the PR. | ||
| 105 | + | ||
| 106 | + ## Building locally | ||
| 107 | + | ||
| 108 | + From the `ydb-java-examples` repository root: | ||
| 109 | + | ||
| 110 | + ```bash | ||
| 111 | + mvn -pl slo-workload/jdbc -am -DskipTests package | ||
| 112 | + ``` | ||
| 113 | + | ||
| 114 | + The resulting jar is at | ||
| 115 | + `slo-workload/jdbc/target/ydb-slo-jdbc-workload.jar`. To run it against a | ||
| 116 | + local YDB: | ||
| 117 | + | ||
| 118 | + ```bash | ||
| 119 | + export YDB_CONNECTION_STRING="grpc://localhost:2136/local" | ||
| 120 | + export WORKLOAD_REF=local | ||
| 121 | + export WORKLOAD_NAME=java-slo-jdbc | ||
| 122 | + | ||
| 123 | + java -jar slo-workload/jdbc/target/ydb-slo-jdbc-workload.jar \ | ||
| 124 | + --duration 60 --read-rps 100 --write-rps 10 --prefill-count 100 | ||
| 125 | + ``` | ||
| 126 | + | ||
| 127 | + If `OTEL_EXPORTER_OTLP_ENDPOINT` is not set, metrics are still recorded | ||
| 128 | + in-process but never exported — handy for verifying that the workload runs | ||
| 129 | + cleanly before pushing to CI. | ||
| 130 | + | ||
| 131 | + ## Adding a new workload | ||
| 132 | + | ||
| 133 | + 1. Create a module next to `jdbc` (e.g. `spring-data-jpa`). | ||
| 134 | + 2. Reuse `Config`, `Metrics`, and the `kv` package; replace only the data | ||
| 135 | + access layer with the framework under test. | ||
| 136 | + 3. Register the module in `slo-workload/pom.xml` and wire it into the SLO | ||
| 137 | + workflow of the component under test (in its own repository). | ||
| 138 | + | ||
| 139 | + ## Links | ||
| 140 | + | ||
| 141 | + - [SDK SLO workload (reference)](../slo) | ||
| 142 | + - [YDB SLO Action](https://github.com/ydb-platform/ydb-slo-action) | ||
| 143 | + - [YDB JDBC Driver](https://github.com/ydb-platform/ydb-jdbc-driver) | ||
| 144 | + - [YDB Documentation](https://ydb.tech/docs/) | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,51 @@ | |||
| 1 | + # Multi-stage Dockerfile for the YDB JDBC SLO workload. | ||
| 2 | + # | ||
| 3 | + # The image can be consumed by the YDB SLO action | ||
| 4 | + # (`ydb-platform/ydb-slo-action`): the workload reads its connection details | ||
| 5 | + # and run parameters from environment variables and pushes OTLP metrics to the | ||
| 6 | + # endpoint the action provides. | ||
| 7 | + # | ||
| 8 | + # Build context: the `ydb-java-examples` repository root. | ||
| 9 | + # | ||
| 10 | + # Optional build args: | ||
| 11 | + # MAVEN_IMAGE Builder image. Defaults to `maven:3.9-eclipse-temurin-17`. | ||
| 12 | + # RUNTIME_IMAGE Runtime image. Defaults to `eclipse-temurin:17-jre`. | ||
| 13 | + # YDB_JDBC_VERSION Override the ydb-jdbc-driver version under test. | ||
| 14 | + | ||
| 15 | + ARG MAVEN_IMAGE=maven:3.9-eclipse-temurin-17 | ||
| 16 | + ARG RUNTIME_IMAGE=eclipse-temurin:17-jre | ||
| 17 | + | ||
| 18 | + # ---------- builder --------------------------------------------------------- | ||
| 19 | + FROM ${MAVEN_IMAGE} AS workload-build | ||
| 20 | + | ||
| 21 | + WORKDIR /src | ||
| 22 | + COPY . /src | ||
| 23 | + | ||
| 24 | + ARG YDB_JDBC_VERSION="" | ||
| 25 | + | ||
| 26 | + # Pin the JDBC driver version under test when provided, then build only the | ||
| 27 | + # workload module (and the parent context it needs). | ||
| 28 | + RUN if [ -n "${YDB_JDBC_VERSION}" ]; then \ | ||
| 29 | + echo "Pinning ydb-jdbc-driver to ${YDB_JDBC_VERSION}" && \ | ||
| 30 | + mvn -B -q versions:set-property \ | ||
| 31 | + -Dproperty=ydb.jdbc.version \ | ||
| 32 | + -DnewVersion="${YDB_JDBC_VERSION}" \ | ||
| 33 | + -DgenerateBackupPoms=false \ | ||
| 34 | + -pl slo-workload ; \ | ||
| 35 | + fi && \ | ||
| 36 | + mvn -B -q -pl slo-workload/jdbc -am \ | ||
| 37 | + -DskipTests \ | ||
| 38 | + -Dmaven.javadoc.skip=true \ | ||
| 39 | + package | ||
| 40 | + | ||
| 41 | + # ---------- runtime --------------------------------------------------------- | ||
| 42 | + FROM ${RUNTIME_IMAGE} | ||
| 43 | + | ||
| 44 | + WORKDIR /app | ||
| 45 | + | ||
| 46 | + # The jar's manifest Class-Path points at libs/, so a single `java -jar` call | ||
| 47 | + # is enough. | ||
| 48 | + COPY --from=workload-build /src/slo-workload/jdbc/target/ydb-slo-jdbc-workload.jar /app/ydb-slo-jdbc-workload.jar | ||
| 49 | + COPY --from=workload-build /src/slo-workload/jdbc/target/libs /app/libs | ||
| 50 | + | ||
| 51 | + ENTRYPOINT ["java", "-jar", "/app/ydb-slo-jdbc-workload.jar"] | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,91 @@ | |||
| 1 | + # JDBC SLO workload | ||
| 2 | + | ||
| 3 | + A plain-JDBC SLO workload that exercises the | ||
| 4 | + [YDB JDBC driver](https://github.com/ydb-platform/ydb-jdbc-driver) under load | ||
| 5 | + and chaos. It mirrors the structure and metrics contract of the SDK SLO | ||
| 6 | + workload in [`../../slo`](../../slo), so reports are directly comparable. | ||
| 7 | + | ||
| 8 | + > See the [parent README](../README.md) for the shared metrics, environment | ||
| 9 | + > variables, CLI flags and CI flow. | ||
| 10 | + | ||
| 11 | + ## What it does | ||
| 12 | + | ||
| 13 | + The workload runs as a standalone jar (`tech.ydb.slo.Main`) and goes through | ||
| 14 | + three phases against a partitioned KV table: | ||
| 15 | + | ||
| 16 | + 1. **Setup** — `CREATE TABLE IF NOT EXISTS` plus a prefill of `--prefill-count` | ||
| 17 | + rows. | ||
| 18 | + 2. **Run** — dedicated read and write thread pools, each paced by a Guava | ||
| 19 | + `RateLimiter` to the target RPS, running until the configured duration. | ||
| 20 | + 3. **Teardown** — `DROP TABLE`. | ||
| 21 | + | ||
| 22 | + Every worker thread owns its own JDBC `Connection` (the driver's connections | ||
| 23 | + are not thread-safe) and reuses prepared statements. On a connection-level | ||
| 24 | + error the connection is transparently reopened on the next attempt. | ||
| 25 | + | ||
| 26 | + ## Schema | ||
| 27 | + | ||
| 28 | + ``` | ||
| 29 | + hash Uint64 -- primary key, derived from id on the client | ||
| 30 | + id Uint64 -- primary key | ||
| 31 | + payload_str Utf8 | ||
| 32 | + payload_double Double | ||
| 33 | + payload_timestamp Timestamp | ||
| 34 | + payload_hash Uint64 | ||
| 35 | + ``` | ||
| 36 | + | ||
| 37 | + The primary-key `hash` column is derived from `id` with a SplitMix64-style mix | ||
| 38 | + (`KvWorkload#numericHash`) so reads and writes target the same key without | ||
| 39 | + relying on server-side YQL builtins inside parameterized statements. | ||
| 40 | + | ||
| 41 | + ## Retries | ||
| 42 | + | ||
| 43 | + Operations are retried with exponential backoff (up to 10 attempts). An error | ||
| 44 | + is considered retryable when the driver throws a `SQLRecoverableException` or | ||
| 45 | + `SQLTransientException` (which covers the driver's | ||
| 46 | + `YdbRetryableException`, `YdbConditionallyRetryableException`, | ||
| 47 | + `YdbUnavailbaleException` and `YdbTimeoutException`). The number of retries is | ||
| 48 | + recorded in `sdk_retry_attempts_total`, and the failure reason is reported via | ||
| 49 | + the `error_kind` label on `sdk_errors_total` (using the YDB status code when | ||
| 50 | + available). | ||
| 51 | + | ||
| 52 | + ## Files | ||
| 53 | + | ||
| 54 | + ``` | ||
| 55 | + jdbc/ | ||
| 56 | + ├── Dockerfile | ||
| 57 | + ├── pom.xml | ||
| 58 | + ├── README.md | ||
| 59 | + └── src/main/ | ||
| 60 | + ├── java/tech/ydb/slo/ | ||
| 61 | + │ ├── Config.java Reads env vars, resolves the JDBC URL | ||
| 62 | + │ ├── Main.java Entry point | ||
| 63 | + │ ├── Metrics.java OTLP metrics + HDR histograms | ||
| 64 | + │ └── kv/ | ||
| 65 | + │ ├── KvWorkload.java Setup/run/teardown loop over JDBC | ||
| 66 | + │ ├── KvWorkloadParams.java JCommander-bound CLI flags | ||
| 67 | + │ ├── Row.java Row data class | ||
| 68 | + │ └── RowGenerator.java Random payload generator | ||
| 69 | + └── resources/ | ||
| 70 | + └── log4j2.xml Console logging config | ||
| 71 | + ``` | ||
| 72 | + | ||
| 73 | + ## Building and running locally | ||
| 74 | + | ||
| 75 | + ```bash | ||
| 76 | + # From the repository root | ||
| 77 | + mvn -pl slo-workload/jdbc -am -DskipTests package | ||
| 78 | + | ||
| 79 | + export YDB_CONNECTION_STRING="grpc://localhost:2136/local" | ||
| 80 | + export WORKLOAD_REF=local | ||
| 81 | + export WORKLOAD_NAME=java-slo-jdbc | ||
| 82 | + | ||
| 83 | + java -jar slo-workload/jdbc/target/ydb-slo-jdbc-workload.jar \ | ||
| 84 | + --duration 60 --read-rps 100 --write-rps 10 --prefill-count 100 | ||
| 85 | + ``` | ||
| 86 | + | ||
| 87 | + Build the container image (context is the repository root): | ||
| 88 | + | ||
| 89 | + ```bash | ||
| 90 | + docker build -f slo-workload/jdbc/Dockerfile -t ydb-slo-jdbc-workload . | ||
| 91 | + ``` | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,102 @@ | |||
| 1 | + <project xmlns="http://maven.apache.org/POM/4.0.0" | ||
| 2 | + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| 3 | + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 | ||
| 4 | + http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| 5 | + <modelVersion>4.0.0</modelVersion> | ||
| 6 | + | ||
| 7 | + <parent> | ||
| 8 | + <groupId>tech.ydb.examples</groupId> | ||
| 9 | + <artifactId>slo-workload</artifactId> | ||
| 10 | + <version>1.1.0-SNAPSHOT</version> | ||
| 11 | + <relativePath>../pom.xml</relativePath> | ||
| 12 | + </parent> | ||
| 13 | + | ||
| 14 | + <artifactId>jdbc</artifactId> | ||
| 15 | + <packaging>jar</packaging> | ||
| 16 | + <name>JDBC SLO workload</name> | ||
| 17 | + <description>SLO workload exercising the YDB JDBC driver, compatible with ydb-slo-action</description> | ||
| 18 | + | ||
| 19 | + <dependencies> | ||
| 20 | + <!-- The component under test --> | ||
| 21 | + <dependency> | ||
| 22 | + <groupId>tech.ydb.jdbc</groupId> | ||
| 23 | + <artifactId>ydb-jdbc-driver</artifactId> | ||
| 24 | + </dependency> | ||
| 25 | + | ||
| 26 | + <!-- CLI parameters --> | ||
| 27 | + <dependency> | ||
| 28 | + <groupId>com.beust</groupId> | ||
| 29 | + <artifactId>jcommander</artifactId> | ||
| 30 | + </dependency> | ||
| 31 | + | ||
| 32 | + <!-- Rate limiting for the read/write loops --> | ||
| 33 | + <dependency> | ||
| 34 | + <groupId>com.google.guava</groupId> | ||
| 35 | + <artifactId>guava</artifactId> | ||
| 36 | + </dependency> | ||
| 37 | + | ||
| 38 | + <!-- Latency percentiles --> | ||
| 39 | + <dependency> | ||
| 40 | + <groupId>org.hdrhistogram</groupId> | ||
| 41 | + <artifactId>HdrHistogram</artifactId> | ||
| 42 | + </dependency> | ||
| 43 | + | ||
| 44 | + <!-- OpenTelemetry metrics + OTLP exporter --> | ||
| 45 | + <dependency> | ||
| 46 | + <groupId>io.opentelemetry</groupId> | ||
| 47 | + <artifactId>opentelemetry-api</artifactId> | ||
| 48 | + </dependency> | ||
| 49 | + <dependency> | ||
| 50 | + <groupId>io.opentelemetry</groupId> | ||
| 51 | + <artifactId>opentelemetry-sdk</artifactId> | ||
| 52 | + </dependency> | ||
| 53 | + <dependency> | ||
| 54 | + <groupId>io.opentelemetry</groupId> | ||
| 55 | + <artifactId>opentelemetry-sdk-metrics</artifactId> | ||
| 56 | + </dependency> | ||
| 57 | + <dependency> | ||
| 58 | + <groupId>io.opentelemetry</groupId> | ||
| 59 | + <artifactId>opentelemetry-exporter-otlp</artifactId> | ||
| 60 | + </dependency> | ||
| 61 | + | ||
| 62 | + <!-- Logging --> | ||
| 63 | + <dependency> | ||
| 64 | + <groupId>org.apache.logging.log4j</groupId> | ||
| 65 | + <artifactId>log4j-slf4j2-impl</artifactId> | ||
| 66 | + </dependency> | ||
| 67 | + </dependencies> | ||
| 68 | + | ||
| 69 | + <build> | ||
| 70 | + <finalName>ydb-slo-jdbc-workload</finalName> | ||
| 71 | + <plugins> | ||
| 72 | + <plugin> | ||
| 73 | + <groupId>org.apache.maven.plugins</groupId> | ||
| 74 | + <artifactId>maven-compiler-plugin</artifactId> | ||
| 75 | + <configuration> | ||
| 76 | + <release>17</release> | ||
| 77 | + </configuration> | ||
| 78 | + </plugin> | ||
| 79 | + | ||
| 80 | + <!-- Copy transitive dependencies into target/libs so a single | ||
| 81 | + `java -jar` works (Class-Path points at libs/). --> | ||
| 82 | + <plugin> | ||
| 83 | + <groupId>org.apache.maven.plugins</groupId> | ||
| 84 | + <artifactId>maven-dependency-plugin</artifactId> | ||
| 85 | + </plugin> | ||
| 86 | + | ||
| 87 | + <plugin> | ||
| 88 | + <groupId>org.apache.maven.plugins</groupId> | ||
| 89 | + <artifactId>maven-jar-plugin</artifactId> | ||
| 90 | + <configuration> | ||
| 91 | + <archive> | ||
| 92 | + <manifest> | ||
| 93 | + <addClasspath>true</addClasspath> | ||
| 94 | + <classpathPrefix>libs/</classpathPrefix> | ||
| 95 | + <mainClass>tech.ydb.slo.Main</mainClass> | ||
| 96 | + </manifest> | ||
| 97 | + </archive> | ||
| 98 | + </configuration> | ||
| 99 | + </plugin> | ||
| 100 | + </plugins> | ||
| 101 | + </build> | ||
| 102 | + </project> | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments