galax-io / gatling-jdbc-plugin   0.17.2

Apache License 2.0 GitHub

Gatling plugin for JDBC — SQL queries, batch operations, and stored procedures with HikariCP connection pooling

Scala versions: 2.13

Gatling JDBC Plugin

CI Maven Central codecov License Scala Steward badge

JDBC protocol plugin for Gatling load testing framework. Execute SQL queries, inserts, updates, batch operations, and stored procedures against any JDBC-compatible database with connection pooling (HikariCP) and result checks.

Table of Contents

Compatibility

Plugin Version Gatling Scala Java
0.x.y-latest 3.13.x 2.13 17+
0.x.y 3.11.x 2.13 17+

Branch strategy: main targets Gatling 3.11.x, latest/gatling targets Gatling 3.13.x.

Installation

Scala (sbt)

libraryDependencies += "org.galaxio" %% "gatling-jdbc-plugin" % "<version>" % Test

Java / Kotlin (Gradle Kotlin DSL)

gatling("org.galaxio:gatling-jdbc-plugin_2.13:<version>")

Maven

<dependency>
  <groupId>org.galaxio</groupId>
  <artifactId>gatling-jdbc-plugin_2.13</artifactId>
  <version>${version}</version>
  <scope>test</scope>
</dependency>

Quick Start

Docker (local PostgreSQL)

docker run -d --name gatling-pg \
  -e POSTGRES_USER=test -e POSTGRES_PASSWORD=test -e POSTGRES_DB=test \
  -p 5432:5432 postgres:16

Minimal Scenario — Scala

import org.galaxio.gatling.jdbc.Predef._
import io.gatling.core.Predef._

class JdbcSimulation extends Simulation {
  val dbConf = DB
    .url("jdbc:postgresql://localhost:5432/test")
    .username("test")
    .password("test")
    .maximumPoolSize(10)

  val scn = scenario("JDBC Query")
    .exec(
      jdbc("select users")
        .query("SELECT * FROM users WHERE id = 1")
        .check(allResults.saveAs("rows"))
    )

  setUp(scn.inject(atOnceUsers(1))).protocols(dbConf)
}

Minimal Scenario — Java

import static org.galaxio.gatling.jdbc.javaapi.JdbcDsl.*;
import static io.gatling.javaapi.core.CoreDsl.*;

public class JdbcSimulation extends Simulation {
  var dbConf = DB()
      .url("jdbc:postgresql://localhost:5432/test")
      .username("test")
      .password("test")
      .maximumPoolSize(10)
      .protocolBuilder();

  var scn = scenario("JDBC Query")
      .exec(
          jdbc("select users")
              .query("SELECT * FROM users WHERE id = 1")
              .check(allResults().saveAs("rows"))
      );

  { setUp(scn.injectOpen(atOnceUsers(1)).protocols(dbConf)); }
}

Minimal Scenario — Kotlin

import org.galaxio.gatling.jdbc.javaapi.JdbcDsl.*
import io.gatling.javaapi.core.CoreDsl.*

class JdbcSimulation : Simulation() {
  val dbConf = DB()
      .url("jdbc:postgresql://localhost:5432/test")
      .username("test")
      .password("test")
      .maximumPoolSize(10)
      .protocolBuilder()

  val scn = scenario("JDBC Query")
      .exec(
          jdbc("select users")
              .query("SELECT * FROM users WHERE id = 1")
              .check(allResults().saveAs("rows"))
      )

  init { setUp(scn.injectOpen(atOnceUsers(1)).protocols(dbConf)) }
}

Protocol Configuration

Scala

import org.galaxio.gatling.jdbc.Predef._

val dataBase = DB
  .url("jdbc:postgresql://localhost:5432/test")
  .username("user")
  .password("pass")
  .maximumPoolSize(32)
  .blockingPoolSize(32)
  .queryTimeout(30.seconds)

Java

var dataBase = DB()
    .url("jdbc:postgresql://localhost:5432/test")
    .username("user")
    .password("pass")
    .maximumPoolSize(32)
    .blockingPoolSize(32)
    .queryTimeout(Duration.ofSeconds(30))
    .protocolBuilder();

Kotlin

val dataBase = DB()
    .url("jdbc:postgresql://localhost:5432/test")
    .username("user")
    .password("pass")
    .maximumPoolSize(32)
    .blockingPoolSize(32)
    .queryTimeout(Duration.ofSeconds(30))
    .protocolBuilder()

Connection Pool Settings

Setting Default Description
maximumPoolSize 10 Max connections in HikariCP pool
minimumIdleConnections 10 Min idle connections
blockingPoolSize = maximumPoolSize Fixed thread pool for blocking JDBC calls
connectionTimeout 1 minute Connection acquisition timeout
queryTimeout None Statement query timeout (per query)

JDBC calls are blocking, so the plugin runs them on a dedicated executor. blockingPoolSize defaults to maximumPoolSize to prevent unbounded native thread growth.

Custom HikariConfig

val hikariConfig = new HikariConfig()
hikariConfig.setJdbcUrl("jdbc:postgresql://localhost:5432/test")
hikariConfig.setMaximumPoolSize(16)

val dataBase = DB.hikariConfig(hikariConfig)

Actions

Query

jdbc("select users")
  .query("SELECT * FROM users WHERE status = 'active'")
  .check(allResults.saveAs("rows"))

Parameterized Query

jdbc("find user")
  .queryP("SELECT * FROM users WHERE id = {id} AND status = {status}")
  .params("id" -> "#{userId}", "status" -> "active")

Insert

jdbc("insert user")
  .insertInto("users", "id", "name", "email")
  .values(Map("id" -> 1, "name" -> "#{userName}", "email" -> "#{email}"))

Transaction

Execute multiple SQL statements atomically (auto-rollback on failure):

jdbc("transfer funds").transaction(
  "UPDATE accounts SET balance = balance - 100 WHERE id = 1",
  "UPDATE accounts SET balance = balance + 100 WHERE id = 2",
  "INSERT INTO audit_log (action) VALUES ('transfer')",
)

Checks

Check Description
allResults / allResults() Full result set
row(index) Single row by zero-based index
column(name) All values from a column
cell(name, rowIndex) Single value at column + row
simpleCheck(predicate) Custom boolean predicate

Scala

jdbc("select users")
  .query("SELECT ID AS USER_ID, NAME FROM USERS ORDER BY ID")
  .check(
    cell("NAME", 0).is("Alice"),
    row(0).saveAs("firstRow"),
    column("USER_ID").saveAs("userIds"),
    allResults.saveAs("rows"),
  )

Java

jdbc("select users")
    .query("SELECT ID AS USER_ID, NAME FROM USERS ORDER BY ID")
    .check(
        cell("NAME", 0).saveAs("firstName"),
        row(0).saveAs("firstRow"),
        column("USER_ID").saveAs("userIds"),
        allResults().saveAs("rows")
    );

Session Variables

The plugin supports Gatling Expression Language (EL) in SQL queries. Use #{variableName} to reference session values.

query() with EL

exec(session => session.set("tableName", "USERS"))
.exec(jdbc("dynamic query").query("SELECT * FROM #{tableName} WHERE id = #{id}"))

queryP() — Prepared Statements

  • {param} in SQL — prepared statement placeholder (replaced with ?)
  • "#{var}" in .params() — Gatling EL, resolves from session at runtime
jdbc("parameterized query")
  .queryP("SELECT * FROM users WHERE id = {id}")
  .params("id" -> "#{userId}")

Java/Kotlin Typed Values

jdbc("insert user")
    .insertInto("USERS", "id", "name", "active")
    .values(Map.of(
        "id", 1,
        "name", "#{userName}",
        "active", true
    ));

Transactions

Execute multiple SQL statements atomically:

jdbc("transfer funds").transaction(
  "UPDATE accounts SET balance = balance - 100 WHERE id = 1",
  "UPDATE accounts SET balance = balance + 100 WHERE id = 2",
)

If any statement fails, the entire transaction is rolled back. Gatling EL expressions are supported.

Examples

Contributing

# Build
sbt compile

# Run unit tests
sbt test

# Run integration tests (requires Docker)
sbt "Test / testOnly -- -n org.galaxio.gatling.jdbc.tags.DockerTest"

# Run Gatling example simulation (H2)
sbt "Gatling / testOnly org.galaxio.performance.jdbc.test.DebugTest"

# Check formatting
sbt scalafmtCheckAll

# Format code
sbt scalafmtAll

License

Apache License 2.0. See LICENSE for details.