You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by iv...@apache.org on 2024/04/08 07:33:17 UTC
(ignite-extensions) branch master updated: IGNITE-21679 Add ignite gatling plugin (#257)
This is an automated email from the ASF dual-hosted git repository.
ivandasch pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite-extensions.git
The following commit(s) were added to refs/heads/master by this push:
new e8ebaf22 IGNITE-21679 Add ignite gatling plugin (#257)
e8ebaf22 is described below
commit e8ebaf22718acf1d1851b115de007ead4bac8792
Author: Sergey Korotkov <se...@gmail.com>
AuthorDate: Mon Apr 8 14:33:12 2024 +0700
IGNITE-21679 Add ignite gatling plugin (#257)
---
modules/gatling-ext/README.md | 201 +++++++++++++
.../examples/.mvn/wrapper/maven-wrapper.properties | 19 ++
modules/gatling-ext/examples/README.md | 38 +++
modules/gatling-ext/examples/mvnw | 308 +++++++++++++++++++
modules/gatling-ext/examples/mvnw.cmd | 205 +++++++++++++
modules/gatling-ext/examples/pom.xml | 95 ++++++
.../examples/src/test/resources/gatling-akka.conf | 27 ++
.../examples/src/test/resources/gatling.conf | 144 +++++++++
.../src/test/resources/ignite-thin-config.xml | 30 ++
.../examples/src/test/resources/logback-test.xml | 29 ++
.../examples/src/test/scala/GatlingRunner.scala | 49 +++
.../test/scala/ignite/DslPutAsyncThinPool.scala | 82 +++++
.../src/test/scala/ignite/DslPutGetThinTx.scala | 110 +++++++
.../test/scala/ignite/LambdaPutBinarySelect.scala | 116 +++++++
modules/gatling-ext/gatling-plugin/.gitignore | 5 +
modules/gatling-ext/gatling-plugin/.scalafmt.conf | 42 +++
modules/gatling-ext/gatling-plugin/DEVNOTES.md | 35 +++
modules/gatling-ext/gatling-plugin/pom.xml | 225 ++++++++++++++
.../gatling-plugin/project/license-header | 16 +
.../gatling-plugin/project/scalastyle_config.xml | 333 +++++++++++++++++++++
.../org/apache/ignite/gatling/IgniteDsl.scala | 199 ++++++++++++
.../scala/org/apache/ignite/gatling/Predef.scala | 44 +++
.../apache/ignite/gatling/action/CacheAction.scala | 89 ++++++
.../ignite/gatling/action/IgniteAction.scala | 280 +++++++++++++++++
.../action/cache/ArgumentsResolveSupport.scala | 41 +++
.../gatling/action/cache/CacheGetAction.scala | 64 ++++
.../gatling/action/cache/CacheGetAllAction.scala | 66 ++++
.../action/cache/CacheGetAndPutAction.scala | 68 +++++
.../action/cache/CacheGetAndRemoveAction.scala | 64 ++++
.../gatling/action/cache/CacheInvokeAction.scala | 73 +++++
.../action/cache/CacheInvokeAllAction.scala | 159 ++++++++++
.../gatling/action/cache/CacheLockAction.scala | 74 +++++
.../gatling/action/cache/CachePutAction.scala | 61 ++++
.../gatling/action/cache/CachePutAllAction.scala | 63 ++++
.../gatling/action/cache/CacheRemoveAction.scala | 61 ++++
.../action/cache/CacheRemoveAllAction.scala | 63 ++++
.../gatling/action/cache/CacheSqlAction.scala | 84 ++++++
.../gatling/action/cache/CacheUnlockAction.scala | 61 ++++
.../gatling/action/ignite/CloseClientAction.scala | 55 ++++
.../action/ignite/GetOrCreateCacheAction.scala | 85 ++++++
.../gatling/action/ignite/LambdaAction.scala | 59 ++++
.../gatling/action/ignite/StartClientAction.scala | 52 ++++
.../action/ignite/TransactionCloseAction.scala | 53 ++++
.../action/ignite/TransactionCommitAction.scala | 56 ++++
.../action/ignite/TransactionRollbackAction.scala | 56 ++++
.../action/ignite/TransactionStartAction.scala | 78 +++++
.../org/apache/ignite/gatling/api/CacheApi.scala | 280 +++++++++++++++++
.../org/apache/ignite/gatling/api/IgniteApi.scala | 243 +++++++++++++++
.../apache/ignite/gatling/api/TransactionApi.scala | 46 +++
.../ignite/gatling/api/node/CacheNodeApi.scala | 216 +++++++++++++
.../ignite/gatling/api/node/IgniteNodeApi.scala | 100 +++++++
.../gatling/api/node/TransactionNodeApi.scala | 42 +++
.../ignite/gatling/api/thin/CacheThinApi.scala | 165 ++++++++++
.../ignite/gatling/api/thin/IgniteThinApi.scala | 99 ++++++
.../gatling/api/thin/TransactionThinApi.scala | 41 +++
.../apache/ignite/gatling/api/thin/package.scala | 44 +++
.../ignite/gatling/builder/cache/Cache.scala | 266 ++++++++++++++++
.../builder/cache/CacheActionParameters.scala | 87 ++++++
.../builder/cache/CacheGetActionBuilder.scala | 49 +++
.../builder/cache/CacheGetAllActionBuilder.scala | 51 ++++
.../cache/CacheGetAndPutActionBuilder.scala | 51 ++++
.../cache/CacheGetAndRemoveActionBuilder.scala | 49 +++
.../builder/cache/CacheInvokeActionBuilder.scala | 123 ++++++++
...eInvokeAllMultipleProcessorsActionBuilder.scala | 79 +++++
...acheInvokeAllSingleProcessorActionBuilder.scala | 127 ++++++++
.../builder/cache/CacheLockActionBuilder.scala | 50 ++++
.../builder/cache/CachePutActionBuilder.scala | 48 +++
.../builder/cache/CachePutAllActionBuilder.scala | 50 ++++
.../builder/cache/CacheRemoveActionBuilder.scala | 47 +++
.../cache/CacheRemoveAllActionBuilder.scala | 49 +++
.../builder/cache/CacheSqlActionBuilder.scala | 87 ++++++
.../builder/cache/CacheUnlockActionBuilder.scala | 48 +++
.../builder/ignite/CloseClientActionBuilder.scala | 49 +++
.../ignite/GetOrCreateCacheActionBuilder.scala | 234 +++++++++++++++
.../ignite/gatling/builder/ignite/Ignite.scala | 49 +++
.../builder/ignite/LambdaActionBuilder.scala | 48 +++
.../builder/ignite/StartClientActionBuilder.scala | 49 +++
.../gatling/builder/transaction/Transactions.scala | 296 ++++++++++++++++++
.../ignite/gatling/check/IgniteCheckSupport.scala | 73 +++++
.../gatling/check/IgniteKeyValueCheckSupport.scala | 92 ++++++
.../check/IgniteKeyValueEntriesCheckSupport.scala | 114 +++++++
.../IgniteKeyValueMapResultCheckSupport.scala | 98 ++++++
.../gatling/check/IgniteSqlCheckSupport.scala | 146 +++++++++
.../ignite/gatling/protocol/IgniteClientPool.scala | 83 +++++
.../ignite/gatling/protocol/IgniteComponents.scala | 68 +++++
.../ignite/gatling/protocol/IgniteProtocol.scala | 177 +++++++++++
.../gatling/protocol/IgniteProtocolSupport.scala | 174 +++++++++++
.../gatling-plugin/src/test/resources/ddl.sql | 3 +
.../src/test/resources/ignite-config.xml | 42 +++
.../src/test/resources/ignite-thin-config.xml | 29 ++
.../org/apache/ignite/gatling/BinaryTest.scala | 111 +++++++
.../apache/ignite/gatling/CreateCacheTest.scala | 110 +++++++
.../org/apache/ignite/gatling/InvokeAllTest.scala | 184 ++++++++++++
.../org/apache/ignite/gatling/InvokeTest.scala | 162 ++++++++++
.../org/apache/ignite/gatling/LambdaTest.scala | 120 ++++++++
.../scala/org/apache/ignite/gatling/LockTest.scala | 123 ++++++++
.../org/apache/ignite/gatling/ProtocolTest.scala | 211 +++++++++++++
.../apache/ignite/gatling/PutAllGetAllTest.scala | 133 ++++++++
.../org/apache/ignite/gatling/PutGetTest.scala | 170 +++++++++++
.../scala/org/apache/ignite/gatling/SqlTest.scala | 134 +++++++++
.../gatling/TransactionInvalidAsyncOpsTest.scala | 130 ++++++++
.../gatling/TransactionInvalidParamsTest.scala | 203 +++++++++++++
.../apache/ignite/gatling/TransactionTest.scala | 133 ++++++++
.../ignite/gatling/utils/AbstractGatlingTest.scala | 129 ++++++++
.../ignite/gatling/utils/IgniteSupport.scala | 82 +++++
modules/gatling-ext/pom.xml | 91 ++++++
pom.xml | 1 +
107 files changed, 10805 insertions(+)
diff --git a/modules/gatling-ext/README.md b/modules/gatling-ext/README.md
new file mode 100644
index 00000000..52a51915
--- /dev/null
+++ b/modules/gatling-ext/README.md
@@ -0,0 +1,201 @@
+# Ignite Gatling Plugin
+
+## Introduction
+
+Plugin allowing to run load tests against the [Apache Ignite](https://ignite.apache.org/) clusters using [Gatling](https://gatling.io/) (3.10.x)
+
+Plugin is currently available for Scala.
+
+## Usage
+
+You may add plugin as dependency in project with your tests. Write this to your POM file:
+
+```xml
+ <dependency>
+ <groupId>org.apache.ignite</groupId>
+ <artifactId>ignite-gatling-plugin</artifactId>
+ <version>${version}</version>
+ </dependency>
+```
+
+## Build
+
+To build everything and run all tests and examples from command line do the following:
+
+```
+cd ignite-extensions
+mvn clean package -f modules/gatling-ext
+```
+
+## Examples
+
+See [here](examples) the example project based on the [Gatling Maven Plugin](https://github.com/gatling/gatling-maven-plugin).
+
+## Features
+
+### Ignite Operations DSL
+
+The most popular Ignite operations are supported via the DSL which allows to define the test scenario
+in a way like below:
+
+```scala
+val scn = scenario("PutGetTx")
+ .feed(feeder)
+ .ignite(
+ // Execute in transaction.
+ tx concurrency PESSIMISTIC isolation REPEATABLE_READ timeout 3000 size 2 run (
+ put[Int, Int]("cache", "#{key}", "#{value}") as "txPut",
+
+ get[Int, Int]("cache", "#{key}").check(
+ entries[Int, Int].transform(_.value).is("#{value}")
+ ) as "txGet",
+
+ sql("City", "SELECT * FROM City WHERE Id=?")
+ .args("#{Id}")
+ .check(resultSet.count.is(1)) as "txSelect",
+
+ commit as "txCommit"
+ ) as "transaction"
+ )
+```
+
+If the operations DSL is used each individual operation (like transaction start, put, get and commit in the above example)
+would be measured and represented separately in the final HTML report. Besides, the gatling group will be created for the
+transaction to measure it as a whole.
+
+Names of operations to be used in report are specified via the `as` keyword.
+
+Async operations are also supported via the `async` keyword. The limitation is that it's
+only available outside the transactions.
+
+```scala
+val scn = scenario("PutAsync")
+ .feed(feeder)
+ .ignite(
+ put[Int, Int]("cache", "#{key}", "#{value}") as "put" async
+ )
+```
+
+The following operations are supported by the DSL:
+
+* ignite
+ * start client / close client
+ * getOrCreateCache
+* cache
+ * get / put / remove
+ * getAll / putAll / removeAll
+ * getAndPut / getAndRemove
+ * sql query
+ * invoke / invokeAll
+ * lock / unlock
+* transactions
+ * tx (start transaction)
+ * commit
+ * rollback
+
+See examples for each supported Ignite operations DSL in [unit tests](gatling-plugin/src/test/scala/org/apache/ignite/gatling).
+
+### Ignite Lambda DSL
+
+For more complex scenarios more generic DSL is available. It allows to implement function or lambda
+accepting Ignite or IgniteClient instance and invoke arbitrary Ignite APIs and use it in Gatling scenario.
+
+It may be implemented in Java if Scala is not desirable.
+
+```scala
+
+def ignitePut = { ignite: Ignite =>
+ val cache = ignite.cache("City").withKeepBinary[Int, BinaryObject]()
+
+ val id = ThreadLocalRandom.current().nextInt()
+
+ val value = ignite.binary().builder("City")
+ .setField("Id", id)
+ .setField("Name", ThreadLocalRandom.current().alphanumeric.take(20).mkString)
+ .build()
+
+ cache.put(id, value)
+}
+
+val scn: ScenarioBuilder = scenario("PutBinary")
+ .feed(feeder)
+ .exec {
+ ignitePut as "put"
+ }
+```
+
+Individual operations inside lambda are not measured by Gatling separately. Durations are measured
+and reported for the lambda as a while.
+
+### Ignite connection DSL
+
+Connection to Ignite cluster is configured via the `igniteProtocol` element. Several options are supported.
+
+```scala
+// Ignite node config instance
+val protocol: IgniteProtocol = igniteProtocol.igniteCfg(new IgniteConfiguration().setClientMode(true))
+
+// Ignite node instance
+val protocol: IgniteProtocol = igniteProtocol.ignite(
+ Ignition.start(new IgniteConfiguration().setClientMode(true)))
+
+// Ignite node Spring XML config path:
+val protocol: IgniteProtocol = igniteProtocol.igniteCfgPath("config/default-config.xml")
+
+// Ignite Thin Client config instance:
+val protocol: IgniteProtocol = igniteProtocol.clientCfg(new ClientConfiguration())
+
+// Ignite Thin Client instance
+val protocol: IgniteProtocol = igniteProtocol.client(
+ Ignition.startClient(new ClientConfiguration()))
+
+// Ignite Thin Client Spring XML config path:
+val protocol: IgniteProtocol = igniteProtocol.clientCfgPath("config/ignite-thin-config.xml")
+
+// Some Ignite Thin Client pool implementation
+val protocol: IgniteProtocol = igniteProtocol.clientPool(
+ new IgniteClientPerThreadPool(new ClientConfiguration()))
+```
+
+If configs are used for protocol creation Gatling starts a single instance of client node or thin client before
+the simulation start. This instance is used by all threads invoked by Gatling. In such case the started node or
+client should be closed explicitly in the simulation's *after* sections as:
+
+```scala
+ after {
+ protocol.close()
+ }
+```
+
+If the pre-started node or client is used for protocol creation it also should be closed in the *after* section like:
+```scala
+ val ignite = Ignition.start(new IgniteConfiguration().setClientMode(true))
+
+ val protocol: IgniteProtocol = igniteProtocol.ignite(ignite)
+
+ after {
+ ignite.close()
+ }
+```
+
+If the thin client is used protocol may be created passing the client pool. Pool is an implementation
+of the *org.apache.ignite.gatling.protocol.IgniteClientPool* trait. Gatling requests the pool for the
+thin client instance once it needs one.
+
+There are two predefined pool implementations:
+ * *IgniteClientPerThreadPool* - dedicated client is created per thread invoked by the Gatling.
+ * *IgniteClientFixedSizePool* - random client from the pre-started ones is returned.
+
+If pool is used it should be closed in the simulation's *after* sections as:
+
+```scala
+ val pool = new IgniteClientPerThreadPool(
+ new ClientConfiguration().setAddresses("localhost:10800")
+ )
+
+ val protocol: IgniteProtocol = igniteProtocol.clientPool(pool)
+
+ after {
+ pool.close()
+ }
+```
diff --git a/modules/gatling-ext/examples/.mvn/wrapper/maven-wrapper.properties b/modules/gatling-ext/examples/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 00000000..2fcf632c
--- /dev/null
+++ b/modules/gatling-ext/examples/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1,19 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+#
+
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip
+wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar
diff --git a/modules/gatling-ext/examples/README.md b/modules/gatling-ext/examples/README.md
new file mode 100644
index 00000000..4b982550
--- /dev/null
+++ b/modules/gatling-ext/examples/README.md
@@ -0,0 +1,38 @@
+# Ignite Gatling Plugin Examples
+
+This project contains several examples of Ignite Gatling simulations written
+with the Ignite Gatling Plugin.
+
+It uses the [Gatling Maven Plugin](https://github.com/gatling/gatling-maven-plugin)
+which is a currently recommended way to package and maintain Gatling simulations.
+
+Note, that these example simulations start the Ignite server node by themselves. This is done for simplicity.
+
+In the real testing conditions the Ignite server nodes forming the cluster under the test would be started in some
+other way outside the simulation class.
+
+## Simulations included
+
+* ignite.DslPutAsyncThinPool
+* ignite.DslPutGetThinTx
+* ignite.LambdaPutBinarySelect
+
+## Run simulation from command line
+
+Individual simulation can be run by maven. Its name should be passed as a *gatling.simulationClass* system property.
+
+```bash
+cd modules/gatling-ext
+mvn gatling:test -Dgatling.simulationClass=ignite.LambdaPutBinarySelect
+```
+
+## Run simulation from the IDE
+
+Simulations may also be started from within the IDE:
+
+- Open `modules/gatling-ext/pom.xml` as a project in IDE.
+- Start the *GatlingRunner* object.
+- Choose index of the simulation from the printed list entering it from keyboard and press Enter.
+- Enter the optional run description.
+
+After the simulation finish it would print link to the generated HTML report.
diff --git a/modules/gatling-ext/examples/mvnw b/modules/gatling-ext/examples/mvnw
new file mode 100755
index 00000000..8d937f4c
--- /dev/null
+++ b/modules/gatling-ext/examples/mvnw
@@ -0,0 +1,308 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Apache Maven Wrapper startup batch script, version 3.2.0
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+ if [ -f /usr/local/etc/mavenrc ] ; then
+ . /usr/local/etc/mavenrc
+ fi
+
+ if [ -f /etc/mavenrc ] ; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ] ; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "$(uname)" in
+ CYGWIN*) cygwin=true ;;
+ MINGW*) mingw=true;;
+ Darwin*) darwin=true
+ # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+ # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+ if [ -z "$JAVA_HOME" ]; then
+ if [ -x "/usr/libexec/java_home" ]; then
+ JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME
+ else
+ JAVA_HOME="/Library/Java/Home"; export JAVA_HOME
+ fi
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+ if [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=$(java-config --jre-home)
+ fi
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+ [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] &&
+ JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="$(which javac)"
+ if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=$(which readlink)
+ if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then
+ if $darwin ; then
+ javaHome="$(dirname "\"$javaExecutable\"")"
+ javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac"
+ else
+ javaExecutable="$(readlink -f "\"$javaExecutable\"")"
+ fi
+ javaHome="$(dirname "\"$javaExecutable\"")"
+ javaHome=$(expr "$javaHome" : '\(.*\)/bin')
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+ echo "Error: JAVA_HOME is not defined correctly." >&2
+ echo " We cannot execute $JAVACMD" >&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+ if [ -z "$1" ]
+ then
+ echo "Path not specified to find_maven_basedir"
+ return 1
+ fi
+
+ basedir="$1"
+ wdir="$1"
+ while [ "$wdir" != '/' ] ; do
+ if [ -d "$wdir"/.mvn ] ; then
+ basedir=$wdir
+ break
+ fi
+ # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+ if [ -d "${wdir}" ]; then
+ wdir=$(cd "$wdir/.." || exit 1; pwd)
+ fi
+ # end of workaround
+ done
+ printf '%s' "$(cd "$basedir" || exit 1; pwd)"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ # Remove \r in case we run on Windows within Git Bash
+ # and check out the repository with auto CRLF management
+ # enabled. Otherwise, we may read lines that are delimited with
+ # \r\n and produce $'-Xarg\r' rather than -Xarg due to word
+ # splitting rules.
+ tr -s '\r\n' ' ' < "$1"
+ fi
+}
+
+log() {
+ if [ "$MVNW_VERBOSE" = true ]; then
+ printf '%s\n' "$1"
+ fi
+}
+
+BASE_DIR=$(find_maven_basedir "$(dirname "$0")")
+if [ -z "$BASE_DIR" ]; then
+ exit 1;
+fi
+
+MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR
+log "$MAVEN_PROJECTBASEDIR"
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar"
+if [ -r "$wrapperJarPath" ]; then
+ log "Found $wrapperJarPath"
+else
+ log "Couldn't find $wrapperJarPath, downloading it ..."
+
+ if [ -n "$MVNW_REPOURL" ]; then
+ wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
+ else
+ wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
+ fi
+ while IFS="=" read -r key value; do
+ # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' )
+ safeValue=$(echo "$value" | tr -d '\r')
+ case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;;
+ esac
+ done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
+ log "Downloading from: $wrapperUrl"
+
+ if $cygwin; then
+ wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
+ fi
+
+ if command -v wget > /dev/null; then
+ log "Found wget ... using wget"
+ [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet"
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
+ else
+ wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
+ fi
+ elif command -v curl > /dev/null; then
+ log "Found curl ... using curl"
+ [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent"
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
+ else
+ curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
+ fi
+ else
+ log "Falling back to using Java to download"
+ javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java"
+ javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class"
+ # For Cygwin, switch paths to Windows format before running javac
+ if $cygwin; then
+ javaSource=$(cygpath --path --windows "$javaSource")
+ javaClass=$(cygpath --path --windows "$javaClass")
+ fi
+ if [ -e "$javaSource" ]; then
+ if [ ! -e "$javaClass" ]; then
+ log " - Compiling MavenWrapperDownloader.java ..."
+ ("$JAVA_HOME/bin/javac" "$javaSource")
+ fi
+ if [ -e "$javaClass" ]; then
+ log " - Running MavenWrapperDownloader.java ..."
+ ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath"
+ fi
+ fi
+ fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+# If specified, validate the SHA-256 sum of the Maven wrapper jar file
+wrapperSha256Sum=""
+while IFS="=" read -r key value; do
+ case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;;
+ esac
+done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
+if [ -n "$wrapperSha256Sum" ]; then
+ wrapperSha256Result=false
+ if command -v sha256sum > /dev/null; then
+ if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then
+ wrapperSha256Result=true
+ fi
+ elif command -v shasum > /dev/null; then
+ if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then
+ wrapperSha256Result=true
+ fi
+ else
+ echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available."
+ echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties."
+ exit 1
+ fi
+ if [ $wrapperSha256Result = false ]; then
+ echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2
+ echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2
+ echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2
+ exit 1
+ fi
+fi
+
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME")
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=$(cygpath --path --windows "$CLASSPATH")
+ [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+ MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR")
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+# shellcheck disable=SC2086 # safe args
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ $MAVEN_DEBUG_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/modules/gatling-ext/examples/mvnw.cmd b/modules/gatling-ext/examples/mvnw.cmd
new file mode 100644
index 00000000..f80fbad3
--- /dev/null
+++ b/modules/gatling-ext/examples/mvnw.cmd
@@ -0,0 +1,205 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Apache Maven Wrapper startup batch script, version 3.2.0
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
+if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
+
+FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Found %WRAPPER_JAR%
+ )
+) else (
+ if not "%MVNW_REPOURL%" == "" (
+ SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
+ )
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Couldn't find %WRAPPER_JAR%, downloading it ...
+ echo Downloading from: %WRAPPER_URL%
+ )
+
+ powershell -Command "&{"^
+ "$webclient = new-object System.Net.WebClient;"^
+ "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+ "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+ "}"^
+ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^
+ "}"
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Finished downloading %WRAPPER_JAR%
+ )
+)
+@REM End of extension
+
+@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file
+SET WRAPPER_SHA_256_SUM=""
+FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B
+)
+IF NOT %WRAPPER_SHA_256_SUM%=="" (
+ powershell -Command "&{"^
+ "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^
+ "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^
+ " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^
+ " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^
+ " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^
+ " exit 1;"^
+ "}"^
+ "}"
+ if ERRORLEVEL 1 goto error
+)
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% ^
+ %JVM_CONFIG_MAVEN_PROPS% ^
+ %MAVEN_OPTS% ^
+ %MAVEN_DEBUG_OPTS% ^
+ -classpath %WRAPPER_JAR% ^
+ "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
+ %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
+if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%"=="on" pause
+
+if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
+
+cmd /C exit /B %ERROR_CODE%
diff --git a/modules/gatling-ext/examples/pom.xml b/modules/gatling-ext/examples/pom.xml
new file mode 100644
index 00000000..81c7475b
--- /dev/null
+++ b/modules/gatling-ext/examples/pom.xml
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.ignite</groupId>
+ <artifactId>ignite-gatling-parent-ext</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>ignite-gatling-plugin-examples</artifactId>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>ignite-gatling-plugin</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>ignite-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>ignite-indexing</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>ignite-spring</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>io.gatling.highcharts</groupId>
+ <artifactId>gatling-charts-highcharts</artifactId>
+ <version>${gatling.version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <testSourceDirectory>src/test/scala</testSourceDirectory>
+ <plugins>
+ <plugin>
+ <groupId>io.gatling</groupId>
+ <artifactId>gatling-maven-plugin</artifactId>
+ <configuration>
+ <skip>false</skip>
+ <runMultipleSimulations>true</runMultipleSimulations>
+ </configuration>
+ <executions>
+ <execution>
+ <phase>test</phase>
+ <goals>
+ <goal>test</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>net.alchim31.maven</groupId>
+ <artifactId>scala-maven-plugin</artifactId>
+ <version>${scala-maven-plugin.version}</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>testCompile</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/modules/gatling-ext/examples/src/test/resources/gatling-akka.conf b/modules/gatling-ext/examples/src/test/resources/gatling-akka.conf
new file mode 100644
index 00000000..2e962627
--- /dev/null
+++ b/modules/gatling-ext/examples/src/test/resources/gatling-akka.conf
@@ -0,0 +1,27 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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
+
+akka {
+ actor {
+ default-dispatcher {
+ type = Dispatcher
+ executor = "thread-pool-executor"
+ thread-pool-executor {
+ fixed-pool-size = 16
+ }
+ throughput = 1
+ }
+ }
+}
diff --git a/modules/gatling-ext/examples/src/test/resources/gatling.conf b/modules/gatling-ext/examples/src/test/resources/gatling.conf
new file mode 100644
index 00000000..5e42b13a
--- /dev/null
+++ b/modules/gatling-ext/examples/src/test/resources/gatling.conf
@@ -0,0 +1,144 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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
+
+#########################
+# Gatling Configuration #
+#########################
+
+# This file contains all the settings configurable for Gatling with their default values
+
+gatling {
+ core {
+ #outputDirectoryBaseName = "" # The prefix for each simulation result folder (then suffixed by the report generation timestamp)
+ #runDescription = "" # The description for this simulation run, displayed in each report
+ #encoding = "utf-8" # Encoding to use throughout Gatling for file and string manipulation
+ #simulationClass = "" # The FQCN of the simulation to run (when used in conjunction with noReports, the simulation for which assertions will be validated)
+ #elFileBodiesCacheMaxCapacity = 200 # Cache size for request body EL templates, set to 0 to disable
+ #rawFileBodiesCacheMaxCapacity = 200 # Cache size for request body raw files, set to 0 to disable
+ #rawFileBodiesInMemoryMaxSize = 10240 # Max bite size of raw files to be cached in memory
+ #pebbleFileBodiesCacheMaxCapacity = 200 # Cache size for request body Pebble templates, set to 0 to disable
+ #feederAdaptiveLoadModeThreshold = 100 # File size threshold (in MB). Below load eagerly in memory, above use batch mode with default buffer size
+ #shutdownTimeout = 10000 # Milliseconds to wait for the actor system to shutdown
+ extract {
+ regex {
+ #cacheMaxCapacity = 200 # Cache size for the compiled regexes, set to 0 to disable caching
+ }
+ xpath {
+ #cacheMaxCapacity = 200 # Cache size for the compiled XPath queries, set to 0 to disable caching
+ }
+ jsonPath {
+ #cacheMaxCapacity = 200 # Cache size for the compiled jsonPath queries, set to 0 to disable caching
+ }
+ css {
+ #cacheMaxCapacity = 200 # Cache size for the compiled CSS selectors queries, set to 0 to disable caching
+ }
+ }
+ directory {
+ #simulations = "" # If set, directory where simulation classes are located
+ #resources = "" # If set, directory where resources, such as feeder files and request bodies, are located
+ #reportsOnly = "" # If set, name of report folder to look for in order to generate its report
+ #binaries = "" # If set, name of the folder where compiles classes are located: Defaults to GATLING_HOME/target.
+ #results = results # Name of the folder where all reports folder are located
+ }
+ }
+ socket {
+ #connectTimeout = 10000 # Timeout in millis for establishing a TCP socket
+ #tcpNoDelay = true
+ #soKeepAlive = false # if TCP keepalive configured at OS level should be used
+ #soReuseAddress = false
+ }
+ netty {
+ #useNativeTransport = true # if Netty Linux native transport should be used instead of Java NIO
+ #useIoUring = false # if io_uring should be used instead of epoll if available
+ #allocator = "pooled" # switch to unpooled for unpooled ByteBufAllocator
+ #maxThreadLocalCharBufferSize = 200000 # Netty's default is 16k
+ }
+ ssl {
+ #useOpenSsl = true # if OpenSSL should be used instead of JSSE (only the latter can be debugged with -Djavax.net.debug=ssl)
+ #useOpenSslFinalizers = false # if OpenSSL contexts should be freed with Finalizer or if using RefCounted is fine
+ #handshakeTimeout = 10000 # TLS handshake timeout in millis
+ #useInsecureTrustManager = true # Use an insecure TrustManager that trusts all server certificates
+ #enabledProtocols = [] # Array of enabled protocols for HTTPS, if empty use Netty's defaults
+ #enabledCipherSuites = [] # Array of enabled cipher suites for HTTPS, if empty enable all available ciphers
+ #sessionCacheSize = 0 # SSLSession cache size, set to 0 to use JDK's default
+ #sessionTimeout = 0 # SSLSession timeout in seconds, set to 0 to use JDK's default (24h)
+ #enableSni = true # When set to true, enable Server Name indication (SNI)
+ keyStore {
+ #type = "" # Type of SSLContext's KeyManagers store, possible values are jks and p12
+ #file = "" # Location of SSLContext's KeyManagers store
+ #password = "" # Password for SSLContext's KeyManagers store
+ #algorithm = "" # Algorithm used SSLContext's KeyManagers store, typically RSA
+ }
+ trustStore {
+ #type = "" # Type of SSLContext's TrustManagers store, possible values are jks and p12
+ #file = "" # Location of SSLContext's TrustManagers store
+ #password = "" # Password for SSLContext's TrustManagers store
+ #algorithm = "" # Algorithm used by SSLContext's TrustManagers store, typically RSA
+ }
+ }
+ charting {
+ #noReports = false # When set to true, don't generate HTML reports
+ #maxPlotPerSeries = 1000 # Number of points per graph in Gatling reports
+ #useGroupDurationMetric = false # Switch group timings from cumulated response time to group duration.
+ indicators {
+ lowerBound = 2 # Lower bound for the requests' response time to track in the reports and the console summary
+ higherBound = 4 # Higher bound for the requests' response time to track in the reports and the console summary
+ #percentile1 = 50 # Value for the 1st percentile to track in the reports, the console summary and Graphite
+ #percentile2 = 75 # Value for the 2nd percentile to track in the reports, the console summary and Graphite
+ #percentile3 = 95 # Value for the 3rd percentile to track in the reports, the console summary and Graphite
+ #percentile4 = 99 # Value for the 4th percentile to track in the reports, the console summary and Graphite
+ }
+ }
+ http {
+ #fetchedCssCacheMaxCapacity = 200 # Cache size for CSS parsed content, set to 0 to disable
+ #fetchedHtmlCacheMaxCapacity = 200 # Cache size for HTML parsed content, set to 0 to disable
+ #perUserCacheMaxCapacity = 200 # Per virtual user cache size, set to 0 to disable
+ #warmUpUrl = "https://gatling.io" # The URL to use to warm-up the HTTP stack (blank means disabled)
+ #pooledConnectionIdleTimeout = 60000 # Timeout in millis for a connection to stay idle in the pool
+ #requestTimeout = 60000 # Timeout in millis for performing an HTTP request
+ #enableHostnameVerification = false # When set to true, enable hostname verification: SSLEngine.setHttpsEndpointIdentificationAlgorithm("HTTPS")
+ dns {
+ #queryTimeout = 5000 # Timeout in millis of each DNS query in millis
+ #maxQueriesPerResolve = 6 # Maximum allowed number of DNS queries for a given name resolution
+ }
+ }
+ jms {
+ #replyTimeoutScanPeriod = 1000 # scan period for timed out reply messages
+ }
+ data {
+ #writers = [console, file] # The list of DataWriters to which Gatling write simulation data (currently supported : console, file, graphite)
+ #utcDateTime = true # Print date-times with the UTC zone instead of the System's default
+ console {
+ #light = false # When set to true, displays a light version without detailed request stats
+ #writePeriod = 5 # Write interval, in seconds
+ }
+ file {
+ #bufferSize = 8192 # FileDataWriter's internal data buffer size, in bytes
+ }
+ leak {
+ #noActivityTimeout = 30 # Period, in seconds, for which Gatling may have no activity before considering a leak may be happening
+ }
+ graphite {
+ #light = false # only send the all* stats
+ #host = "localhost" # The host where the Carbon server is located
+ #port = 2003 # The port to which the Carbon server listens to (2003 is default for plaintext, 2004 is default for pickle)
+ #protocol = "tcp" # The protocol used to send data to Carbon (currently supported : "tcp", "udp")
+ #rootPathPrefix = "gatling" # The common prefix of all metrics sent to Graphite
+ #bufferSize = 8192 # Internal data buffer size, in bytes
+ #writePeriod = 1 # Write period, in seconds
+ }
+ enableAnalytics = false # Anonymous Usage Analytics (no tracking), please support
+ }
+}
diff --git a/modules/gatling-ext/examples/src/test/resources/ignite-thin-config.xml b/modules/gatling-ext/examples/src/test/resources/ignite-thin-config.xml
new file mode 100644
index 00000000..980b7d5f
--- /dev/null
+++ b/modules/gatling-ext/examples/src/test/resources/ignite-thin-config.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+-->
+
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
+ <bean class="org.apache.ignite.configuration.ClientConfiguration">
+ <property name="addresses">
+ <list>
+ <value>localhost:10800</value>
+ </list>
+ </property>
+ </bean>
+</beans>
diff --git a/modules/gatling-ext/examples/src/test/resources/logback-test.xml b/modules/gatling-ext/examples/src/test/resources/logback-test.xml
new file mode 100644
index 00000000..9d0c0804
--- /dev/null
+++ b/modules/gatling-ext/examples/src/test/resources/logback-test.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+-->
+
+<configuration>
+ <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <pattern>%d{HH:mm:ss.SSS} [%-5level] %logger{15} - %msg%n%rEx</pattern>
+ </encoder>
+ <immediateFlush>false</immediateFlush>
+ </appender>
+ <root level="WARN">
+ <appender-ref ref="CONSOLE"/>
+ </root>
+</configuration>
diff --git a/modules/gatling-ext/examples/src/test/scala/GatlingRunner.scala b/modules/gatling-ext/examples/src/test/scala/GatlingRunner.scala
new file mode 100644
index 00000000..12cb1d15
--- /dev/null
+++ b/modules/gatling-ext/examples/src/test/scala/GatlingRunner.scala
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+import io.gatling.app.Gatling
+import io.gatling.core.config.GatlingPropertiesBuilder
+
+import java.nio.file.Path
+import java.nio.file.Paths
+import java.util.Objects.requireNonNull
+
+/**
+ * Start this object to run example simulations included into this project.
+ *
+ * After start choose a simulation by number entering it from keyboard.
+ */
+object GatlingRunner extends App {
+ val props = new GatlingPropertiesBuilder()
+ .resourcesDirectory(IDEPathHelper.mavenResourcesDirectory.toString)
+ .resultsDirectory(IDEPathHelper.resultsDirectory.toString)
+ .binariesDirectory(IDEPathHelper.mavenBinariesDirectory.toString)
+
+ Gatling.fromMap(props.build)
+}
+
+object IDEPathHelper {
+ private val projectRootDir = Paths.get(requireNonNull(getClass.getResource("gatling.conf"), "Couldn't locate gatling.conf").toURI).getParent.getParent.getParent
+ private val mavenTargetDirectory = projectRootDir.resolve("target")
+ private val mavenSrcTestDirectory = projectRootDir.resolve("src").resolve("test")
+
+ val mavenSourcesDirectory: Path = mavenSrcTestDirectory.resolve("scala")
+ val mavenResourcesDirectory: Path = mavenSrcTestDirectory.resolve("resources")
+ val mavenBinariesDirectory: Path = mavenTargetDirectory.resolve("test-classes")
+ val resultsDirectory: Path = mavenTargetDirectory.resolve("gatling")
+ val recorderConfigFile: Path = mavenResourcesDirectory.resolve("recorder.conf")
+}
diff --git a/modules/gatling-ext/examples/src/test/scala/ignite/DslPutAsyncThinPool.scala b/modules/gatling-ext/examples/src/test/scala/ignite/DslPutAsyncThinPool.scala
new file mode 100644
index 00000000..8e6bd0e8
--- /dev/null
+++ b/modules/gatling-ext/examples/src/test/scala/ignite/DslPutAsyncThinPool.scala
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 ignite
+
+import io.gatling.core.Predef._
+import io.gatling.core.feeder.Feeder
+import io.gatling.core.structure.ScenarioBuilder
+import io.netty.util.internal.ThreadLocalRandom
+import org.apache.ignite.configuration.ClientConfiguration
+import org.apache.ignite.gatling.Predef._
+import org.apache.ignite.gatling.protocol.IgniteClientPerThreadPool
+import org.apache.ignite.gatling.protocol.IgniteProtocol
+import org.apache.ignite.Ignite
+import org.apache.ignite.Ignition
+
+import scala.concurrent.duration.DurationInt
+import scala.language.postfixOps
+
+/**
+ * Ignite gatling simulation example demonstrating:
+ *
+ * - Test scenario defined via the Ignite operations DSL.
+ * - Use async variant of Ignite APIs.
+ * - Ignite gatling protocol configured with thin Ignite client pool creating one client per thread.
+ * - Generate maximum possible load using fixed number of threads (16).
+ */
+class DslPutAsyncThinPool extends Simulation {
+ // Start server ignite node before the simulation start.
+ //
+ // It is started at the same JVM the simulation would run just for simplicity.
+ // In the real world testing conditions server ignite cluster nodes forming the cluster
+ // under test would be started in some other way, outside the simulation class.
+ private val server: Ignite = Ignition.start()
+
+ // Ensure Gatling engine creates enough threads.
+ System.setProperty("io.netty.eventLoopThreads", "16")
+
+ val feeder: Feeder[Int] = Iterator.continually(Map(
+ "key" -> ThreadLocalRandom.current().nextInt(10000),
+ "value" -> ThreadLocalRandom.current().nextInt()
+ ))
+
+ val scn: ScenarioBuilder = scenario("PutAsyncThinPool")
+ .feed(feeder)
+ .ignite(
+ getOrCreateCache("cache") as "Get or create cache",
+
+ put[Int, Int]("cache", "#{key}", "#{value}") as "Put" async,
+ )
+
+ val pool = new IgniteClientPerThreadPool(
+ new ClientConfiguration().setAddresses("localhost:10800")
+ )
+
+ val protocol: IgniteProtocol = igniteProtocol.clientPool(pool)
+
+ after {
+ pool.close()
+
+ server.close()
+ }
+
+ setUp(scn.inject(
+ // Generate maximum possible load running 16 threads
+ constantConcurrentUsers(16) during 30.seconds
+ )).protocols(protocol)
+ .assertions(global.failedRequests.count.is(0))
+}
diff --git a/modules/gatling-ext/examples/src/test/scala/ignite/DslPutGetThinTx.scala b/modules/gatling-ext/examples/src/test/scala/ignite/DslPutGetThinTx.scala
new file mode 100644
index 00000000..fb07f394
--- /dev/null
+++ b/modules/gatling-ext/examples/src/test/scala/ignite/DslPutGetThinTx.scala
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 ignite
+
+import io.gatling.core.Predef._
+import io.gatling.core.feeder.Feeder
+import io.gatling.core.structure.ScenarioBuilder
+import io.netty.util.internal.ThreadLocalRandom
+import org.apache.ignite.gatling.Predef._
+import org.apache.ignite.gatling.protocol.IgniteProtocol
+import org.apache.ignite.Ignite
+import org.apache.ignite.Ignition
+
+import scala.concurrent.duration.DurationInt
+import scala.language.postfixOps
+
+/**
+ * Ignite gatling simulation example demonstrating:
+ *
+ * - Test scenario defined via the Ignite operations DSL.
+ * - Group Ignite operations in transaction.
+ * - Ignite gatling protocol configured via URL of the thin Ignite client Spring XML config file.
+ * - Generate load with fixed RPS (100).
+ *
+ * Before run simulation start the Ignite server node manually on the localhost.
+ */
+class DslPutGetThinTx extends Simulation {
+ // Start server ignite node before the simulation start.
+ //
+ // It is started at the same JVM the simulation would run just for simplicity.
+ // In the real world testing conditions server ignite cluster nodes forming the cluster
+ // under test would be started in some other way, outside the simulation class.
+ private val server: Ignite = Ignition.start()
+
+ /**
+ * Feeder generating random test data for keys and values.
+ */
+ val feeder: Feeder[Int] = Iterator.continually(Map(
+ "key" -> ThreadLocalRandom.current().nextInt(10000),
+ "value" -> ThreadLocalRandom.current().nextInt()
+ ))
+
+ /**
+ * Testing scenario defined via the Ignite operations DSL.
+ *
+ * Each operation like transaction start, put, get, commit will be measured and
+ * represented in final HTML report separately.
+ *
+ * Besides, the gatling group will be created for transaction to measure it as a whole.
+ */
+ val scn: ScenarioBuilder = scenario("PutGetThinTx")
+ .feed(feeder)
+ .ignite(
+ // Create cache if not exists.
+ getOrCreateCache("cache") backups 1 atomicity TRANSACTIONAL mode PARTITIONED
+ as "getOrCreateCache",
+
+ // Setup and start the transaction.
+ tx concurrency PESSIMISTIC isolation REPEATABLE_READ run (
+
+ // Put random value.
+ put[Int, Int]("cache", "#{key}", "#{value}") as "txPut",
+
+ // Get and check that it's the same value as was put.
+ get[Int, Int]("cache", "#{key}")
+ .check(
+ entries[Int, Int].transform(_.value).is("#{value}")
+ ) as "txGet",
+
+ // Commit the transaction.
+ commit as "txCommit"
+
+ ) as "transaction"
+ )
+
+ // Create Ignite gatling protocol passing the path to thin client Spring XML configuration.
+ val protocol: IgniteProtocol = igniteProtocol
+ .clientCfgPath(
+ Thread.currentThread().getContextClassLoader.getResource("ignite-thin-config.xml")
+ )
+
+ // Destroy the client node after the simulation.
+ after {
+ protocol.close()
+
+ server.close()
+ }
+
+ setUp(
+ scn.inject(
+ // Generate constant 100 rps load for 30 seconds.
+ constantUsersPerSec(100) during 30.seconds,
+ )
+ ).protocols(protocol)
+ .assertions(global.failedRequests.count.is(0))
+}
diff --git a/modules/gatling-ext/examples/src/test/scala/ignite/LambdaPutBinarySelect.scala b/modules/gatling-ext/examples/src/test/scala/ignite/LambdaPutBinarySelect.scala
new file mode 100644
index 00000000..d486baec
--- /dev/null
+++ b/modules/gatling-ext/examples/src/test/scala/ignite/LambdaPutBinarySelect.scala
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 ignite
+
+import io.gatling.core.Predef._
+import org.apache.ignite.configuration.IgniteConfiguration
+import org.apache.ignite.gatling.Predef._
+import org.apache.ignite.gatling.protocol.IgniteProtocol
+import org.apache.ignite.Ignite
+import org.apache.ignite.Ignition
+import org.apache.ignite.binary.BinaryObject
+import org.apache.ignite.cache.query.SqlFieldsQuery
+import org.apache.ignite.internal.IgniteEx
+
+import java.util.concurrent.ThreadLocalRandom
+import scala.concurrent.duration.DurationInt
+import scala.language.postfixOps
+import scala.util.Random.javaRandomToRandom
+
+/**
+ * Ignite gatling simulation example demonstrating:
+ *
+ * - Test scenario steps defined both via the Ignite Operations DSL and Ignite Lambda DSL.
+ * - DSL for SQL queries.
+ * - Ignite gatling protocol configured via pre-started Ignite client node.
+ * - Generate load with rising and decreasing RPS (0 -> 100 -> 0).
+ */
+class LambdaPutBinarySelect extends Simulation {
+ // Start server ignite node before the simulation start.
+ //
+ // It is started at the same JVM the simulation would run just for simplicity.
+ // In the real world testing conditions server ignite cluster nodes forming the cluster
+ // under test would be started in some other way, outside the simulation class.
+ private val server: Ignite = Ignition.start()
+
+ // Start ignite client node to be used to generate the load.
+ private val client = Ignition.start(new IgniteConfiguration().setIgniteInstanceName("client").setClientMode(true))
+
+ // Create test table before the simulation start.
+ client.asInstanceOf[IgniteEx].context().query().querySqlFields(new SqlFieldsQuery(
+ """
+ CREATE TABLE IF NOT EXISTS City (
+ Id INT(11),
+ Name CHAR(35),
+ PRIMARY KEY (ID)
+ ) WITH "backups=1, cache_name=City, key_type=Integer, value_type=City";
+ """), false)
+
+ // Ignite put via binary object operation as Lambda.
+ private val putOperation = { (ignite: Ignite, session: Session) =>
+ val cache = ignite.cache("City").withKeepBinary[Int, BinaryObject]()
+
+ val id = session("Id").as[Int]
+
+ val value = ignite.binary().builder("City")
+ .setField("Id", id)
+ .setField("Name", ThreadLocalRandom.current().alphanumeric.take(20).mkString)
+ .build()
+
+ cache.put(id, value)
+ }
+
+ // Ignite SQL operation defined via the DSL.
+ private val selectOperation =
+ sql("City", "SELECT * FROM City WHERE Id=?")
+ .args("#{Id}")
+ .check(resultSet.count.is(1))
+
+ private val feeder = Iterator.continually(Map(
+ "Id" -> ThreadLocalRandom.current().nextInt()
+ ))
+
+ // Scenario calling both put and SQL select.
+ private val scn = scenario("PutBinarySelect")
+ .feed(feeder)
+ .ignite (
+ putOperation as "put",
+ selectOperation as "select"
+ )
+
+ // Create protocol using the pre-started ignite client node.
+ val protocol: IgniteProtocol = igniteProtocol.ignite(client)
+
+ after {
+ // Close ignite client node simulation end.
+ client.close()
+
+ // Close ignite server node simulation end.
+ server.close()
+ }
+
+ setUp(
+ scn.inject(
+ rampUsersPerSec(0) to 100 during 10.seconds,
+
+ constantUsersPerSec(100) during 20.seconds,
+
+ rampUsersPerSec(100) to 0 during 10.seconds
+ )
+ ).protocols(protocol)
+ .assertions(global.failedRequests.count.is(0))
+}
diff --git a/modules/gatling-ext/gatling-plugin/.gitignore b/modules/gatling-ext/gatling-plugin/.gitignore
new file mode 100644
index 00000000..8632b1fd
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/.gitignore
@@ -0,0 +1,5 @@
+results
+target
+*.iml
+.idea
+./ignite/*
diff --git a/modules/gatling-ext/gatling-plugin/.scalafmt.conf b/modules/gatling-ext/gatling-plugin/.scalafmt.conf
new file mode 100644
index 00000000..599a2201
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/.scalafmt.conf
@@ -0,0 +1,42 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+#
+
+version=3.8.0
+runner.dialect = scala213
+
+maxColumn = 140
+
+indent.main = 4
+indent.callSite = 4
+indent.defnSite = 4
+
+assumeStandardLibraryStripMargin = true
+
+docstrings.forceBlankLineBefore = false
+docstrings.style = Asterisk
+docstrings.wrap = no
+
+rewrite.rules = [Imports, RedundantBraces, SortModifiers]
+rewrite.imports.sort = scalastyle
+rewrite.imports.expand = true
+rewrite.imports.groups = [
+ ["java\\..*"],
+ ["javax\\..*"],
+ ["scala\\..*"]
+]
+
+newlines.source=keep
diff --git a/modules/gatling-ext/gatling-plugin/DEVNOTES.md b/modules/gatling-ext/gatling-plugin/DEVNOTES.md
new file mode 100644
index 00000000..a4378430
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/DEVNOTES.md
@@ -0,0 +1,35 @@
+# Ignite Gatling Plugin
+
+## Build
+
+```bash
+mvn clean package -DskipTests
+```
+
+## Run tests
+
+```bash
+mvn test
+```
+
+## Coding style
+
+Check coding style
+
+```bash
+mvn scalastyle:check -Pcheckstyle
+```
+
+
+Preview automatic code formatting changes:
+
+```bash
+mvn spotless:check
+```
+
+
+Apply automatic code formatting:
+
+```bash
+mvn spotless:apply
+```
diff --git a/modules/gatling-ext/gatling-plugin/pom.xml b/modules/gatling-ext/gatling-plugin/pom.xml
new file mode 100644
index 00000000..4d3d353d
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/pom.xml
@@ -0,0 +1,225 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.ignite</groupId>
+ <artifactId>ignite-gatling-parent-ext</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>ignite-gatling-plugin</artifactId>
+
+ <description>Apache Ignite plugin for performance testing with Gatling</description>
+
+ <properties>
+ <scalaz.version>7.3.8</scalaz.version>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>ignite-core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>io.gatling</groupId>
+ <artifactId>gatling-core</artifactId>
+ <version>${gatling.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.scalaz</groupId>
+ <artifactId>scalaz-core_2.13</artifactId>
+ <version>${scalaz.version}</version>
+ </dependency>
+
+ <!-- Test -->
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>ignite-core</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>ignite-spring</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>ignite-indexing</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>io.gatling</groupId>
+ <artifactId>gatling-app</artifactId>
+ <version>${gatling.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>io.gatling.highcharts</groupId>
+ <artifactId>gatling-charts-highcharts</artifactId>
+ <version>${gatling.version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <sourceDirectory>src/main/scala</sourceDirectory>
+ <testSourceDirectory>src/test/scala</testSourceDirectory>
+ <plugins>
+ <plugin>
+ <groupId>net.alchim31.maven</groupId>
+ <artifactId>scala-maven-plugin</artifactId>
+ <version>${scala-maven-plugin.version}</version>
+ <configuration>
+ <args>
+ <arg>-deprecation</arg>
+ <arg>-feature</arg>
+ <arg>-language:implicitConversions</arg>
+ </args>
+ </configuration>
+ <executions>
+ <execution>
+ <id>scaladoc</id>
+ <phase>prepare-package</phase>
+ <goals>
+ <goal>doc</goal>
+ <goal>doc-jar</goal>
+ </goals>
+ <configuration>
+ <doctitle>Ignite Gatling Plugin</doctitle>
+ <skip>${maven.javadoc.skip}</skip>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>com.diffplug.spotless</groupId>
+ <artifactId>spotless-maven-plugin</artifactId>
+ <version>2.22.8</version>
+ <dependencies>
+ <dependency>
+ <groupId>org.scalameta</groupId>
+ <artifactId>scalafmt-core_2.13</artifactId>
+ <version>3.8.0</version>
+ </dependency>
+ </dependencies>
+ <configuration>
+ <scala>
+ <scalafmt>
+ <version>3.8.0</version>
+ <file>${project.basedir}/.scalafmt.conf</file>
+ </scalafmt>
+ <licenseHeader>
+ <file>${project.basedir}/project/license-header</file>
+ <delimiter>package</delimiter>
+ </licenseHeader>
+ </scala>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>prepare-package</phase>
+ <goals>
+ <goal>copy-dependencies</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${project.build.directory}/libs</outputDirectory>
+ <includeScope>runtime</includeScope>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <configuration>
+ <skip>false</skip>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <profiles>
+ <profile>
+ <id>checkstyle</id>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.scalastyle</groupId>
+ <artifactId>scalastyle-maven-plugin</artifactId>
+ <version>1.0.0</version>
+ <configuration>
+ <verbose>false</verbose>
+ <failOnViolation>true</failOnViolation>
+ <includeTestSourceDirectory>true</includeTestSourceDirectory>
+ <failOnWarning>true</failOnWarning>
+ <sourceDirectory>${project.basedir}/src/main/scala</sourceDirectory>
+ <testSourceDirectory>${project.basedir}/src/test/scala</testSourceDirectory>
+ <configLocation>${project.basedir}/project/scalastyle_config.xml</configLocation>
+ <outputFile>${project.basedir}/target/checkstyle-result.xml</outputFile>
+ <outputEncoding>UTF-8</outputEncoding>
+ </configuration>
+ <executions>
+ <execution>
+ <id>style</id>
+ <goals>
+ <goal>check</goal>
+ </goals>
+ <phase>validate</phase>
+ </execution>
+ </executions>
+ <dependencies>
+ <dependency>
+ <groupId>com.beautiful-scala</groupId>
+ <artifactId>scalastyle_2.13</artifactId>
+ <version>1.5.1</version>
+ </dependency>
+ </dependencies>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-checkstyle-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+</project>
diff --git a/modules/gatling-ext/gatling-plugin/project/license-header b/modules/gatling-ext/gatling-plugin/project/license-header
new file mode 100644
index 00000000..ae6f28c4
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/project/license-header
@@ -0,0 +1,16 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
diff --git a/modules/gatling-ext/gatling-plugin/project/scalastyle_config.xml b/modules/gatling-ext/gatling-plugin/project/scalastyle_config.xml
new file mode 100644
index 00000000..3eaa8405
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/project/scalastyle_config.xml
@@ -0,0 +1,333 @@
+<scalastyle commentFilter="enabled">
+ <name>Scalastyle standard configuration</name>
+ <check class="org.scalastyle.file.FileTabChecker" level="warning" enabled="true" />
+ <check class="org.scalastyle.file.FileLengthChecker" level="warning" enabled="true">
+ <parameters>
+ <parameter name="maxFileLength">
+ <![CDATA[800]]>
+ </parameter>
+ </parameters>
+ </check>
+ <check class="org.scalastyle.file.HeaderMatchesChecker" level="warning" enabled="true">
+ <parameters>
+ <parameter name="header"><![CDATA[/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */]]></parameter>
+ <parameter name="regex">
+ <![CDATA[false]]>
+ </parameter>
+ </parameters>
+ </check>
+ <check class="org.scalastyle.scalariform.SpacesAfterPlusChecker" level="warning" enabled="true" />
+ <check class="org.scalastyle.file.WhitespaceEndOfLineChecker" level="warning" enabled="true">
+ <parameters>
+ <parameter name="ignoreWhitespaceLines">
+ <![CDATA[false]]>
+ </parameter>
+ </parameters>
+ </check>
+ <check class="org.scalastyle.scalariform.SpacesBeforePlusChecker" level="warning" enabled="true" />
+ <check class="org.scalastyle.file.FileLineLengthChecker" level="warning" enabled="true">
+ <parameters>
+ <parameter name="maxLineLength">
+ <![CDATA[160]]>
+ </parameter>
+ <parameter name="tabSize">
+ <![CDATA[4]]>
+ </parameter>
+ <parameter name="ignoreImports">
+ <![CDATA[false]]>
+ </parameter>
+ </parameters>
+ </check>
+ <check class="org.scalastyle.scalariform.ClassNamesChecker" level="warning" enabled="true">
+ <parameters>
+ <parameter name="regex">
+ <![CDATA[^[A-Z][A-Za-z]*$]]>
+ </parameter>
+ </parameters>
+ </check>
+ <check class="org.scalastyle.scalariform.ObjectNamesChecker" level="warning" enabled="true">
+ <parameters>
+ <parameter name="regex">
+ <![CDATA[^[A-Z][A-Za-z]*$]]>
+ </parameter>
+ </parameters>
+ </check>
+ <check class="org.scalastyle.scalariform.PackageNamesChecker" level="warning" enabled="true">
+ <parameters>
+ <parameter name="regex">
+ <![CDATA[^[a-z]+$]]>
+ </parameter>
+ </parameters>
+ </check>
+ <check class="org.scalastyle.scalariform.PackageObjectNamesChecker" level="warning" enabled="true">
+ <parameters>
+ <parameter name="regex">
+ <![CDATA[^[a-z][A-Za-z]*$]]>
+ </parameter>
+ </parameters>
+ </check>
+ <check class="org.scalastyle.scalariform.EqualsHashCodeChecker" level="warning" enabled="true" />
+ <check class="org.scalastyle.scalariform.IllegalImportsChecker" level="warning" enabled="true">
+ <parameters>
+ <parameter name="illegalImports">
+ <![CDATA[sun._,java.awt._]]>
+ </parameter>
+ </parameters>
+ </check>
+ <check class="org.scalastyle.scalariform.ParameterNumberChecker" level="warning" enabled="true">
+ <parameters>
+ <parameter name="maxParameters">
+ <![CDATA[8]]>
+ </parameter>
+ </parameters>
+ </check>
+ <check class="org.scalastyle.scalariform.MagicNumberChecker" level="warning" enabled="false">
+ <parameters>
+ <parameter name="ignore">
+ <![CDATA[-1,0,1,2,3]]>
+ </parameter>
+ </parameters>
+ </check>
+ <check class="org.scalastyle.scalariform.NoWhitespaceBeforeLeftBracketChecker" level="warning" enabled="true" />
+ <check class="org.scalastyle.scalariform.NoWhitespaceAfterLeftBracketChecker" level="warning" enabled="true" />
+ <check class="org.scalastyle.scalariform.NoWhitespaceBeforeRightBracketChecker" level="warning" enabled="true" />
+ <check class="org.scalastyle.scalariform.ReturnChecker" level="warning" enabled="true" />
+ <check class="org.scalastyle.scalariform.NullChecker" level="warning" enabled="true">
+ <parameters>
+ <parameter name="allowNullChecks">
+ <![CDATA[true]]>
+ </parameter>
+ </parameters>
+ </check>
+ <check class="org.scalastyle.scalariform.NoCloneChecker" level="warning" enabled="true" />
+ <check class="org.scalastyle.scalariform.NoFinalizeChecker" level="warning" enabled="true" />
+ <check class="org.scalastyle.scalariform.CovariantEqualsChecker" level="warning" enabled="true" />
+ <check class="org.scalastyle.scalariform.StructuralTypeChecker" level="warning" enabled="true" />
+ <check class="org.scalastyle.file.RegexChecker" level="warning" enabled="true">
+ <parameters>
+ <parameter name="regex">
+ <![CDATA[println]]>
+ </parameter>
+ <parameter name="line">
+ <![CDATA[false]]>
+ </parameter>
+ </parameters>
+ </check>
+ <check class="org.scalastyle.scalariform.NumberOfTypesChecker" level="warning" enabled="true">
+ <parameters>
+ <parameter name="maxTypes">
+ <![CDATA[30]]>
+ </parameter>
+ </parameters>
+ </check>
+ <check class="org.scalastyle.scalariform.CyclomaticComplexityChecker" level="warning" enabled="true">
+ <parameters>
+ <parameter name="maximum">
+ <![CDATA[10]]>
+ </parameter>
+ <parameter name="countCases">
+ <![CDATA[true]]>
+ </parameter>
+ </parameters>
+ </check>
+ <check class="org.scalastyle.scalariform.UppercaseLChecker" level="warning" enabled="true" />
+ <check class="org.scalastyle.scalariform.SimplifyBooleanExpressionChecker" level="warning" enabled="true" />
+ <check class="org.scalastyle.scalariform.IfBraceChecker" level="warning" enabled="true">
+ <parameters>
+ <parameter name="singleLineAllowed">
+ <![CDATA[true]]>
+ </parameter>
+ <parameter name="doubleLineAllowed">
+ <![CDATA[false]]>
+ </parameter>
+ </parameters>
+ </check>
+ <check class="org.scalastyle.scalariform.MethodLengthChecker" level="warning" enabled="true">
+ <parameters>
+ <parameter name="maxLength">
+ <![CDATA[50]]>
+ </parameter>
+ <parameter name="ignoreComments">
+ <![CDATA[false]]>
+ </parameter>
+ <parameter name="ignoreEmpty">
+ <![CDATA[false]]>
+ </parameter>
+ </parameters>
+ </check>
+ <check class="org.scalastyle.scalariform.MethodNamesChecker" level="warning" enabled="true">
+ <parameters>
+ <parameter name="regex">
+ <![CDATA[^[a-z][A-Za-z0-9]*(_=)?$]]>
+ </parameter>
+ <parameter name="ignoreRegex">
+ <![CDATA[^$]]>
+ </parameter>
+ <parameter name="ignoreOverride">
+ <![CDATA[false]]>
+ </parameter>
+ </parameters>
+ </check>
+ <check class="org.scalastyle.scalariform.MethodArgumentNamesChecker" level="warning" enabled="true">
+ <parameters>
+ <parameter name="regex">
+ <![CDATA[^[a-z][A-Za-z0-9]*$]]>
+ </parameter>
+ <parameter name="ignoreRegex">
+ <![CDATA[^$]]>
+ </parameter>
+ </parameters>
+ </check>
+ <check class="org.scalastyle.scalariform.NumberOfMethodsInTypeChecker" level="warning" enabled="true">
+ <parameters>
+ <parameter name="maxMethods">
+ <![CDATA[30]]>
+ </parameter>
+ </parameters>
+ </check>
+ <check class="org.scalastyle.scalariform.PublicMethodsHaveTypeChecker" level="warning" enabled="true">
+ <parameters>
+ <parameter name="ignoreOverride">
+ <![CDATA[false]]>
+ </parameter>
+ </parameters>
+ </check>
+ <check class="org.scalastyle.file.NewLineAtEofChecker" level="warning" enabled="true" />
+ <check class="org.scalastyle.file.NoNewLineAtEofChecker" level="warning" enabled="false" />
+ <check class="org.scalastyle.scalariform.NotImplementedErrorUsage" level="warning" enabled="true" />
+ <check class="org.scalastyle.scalariform.WhileChecker" level="warning" enabled="false" />
+ <check class="org.scalastyle.scalariform.VarFieldChecker" level="warning" enabled="false" />
+ <check class="org.scalastyle.scalariform.VarLocalChecker" level="warning" enabled="false" />
+ <check class="org.scalastyle.scalariform.RedundantIfChecker" level="warning" enabled="false" />
+ <check class="org.scalastyle.scalariform.TokenChecker" level="warning" enabled="false">
+ <parameters>
+ <parameter name="regex">
+ <![CDATA[println]]>
+ </parameter>
+ </parameters>
+ </check>
+ <check class="org.scalastyle.scalariform.DeprecatedJavaChecker" level="warning" enabled="true" />
+ <check class="org.scalastyle.scalariform.OverrideJavaChecker" level="warning" enabled="true" />
+ <check class="org.scalastyle.scalariform.EmptyClassChecker" level="warning" enabled="true" />
+ <check class="org.scalastyle.scalariform.ClassTypeParameterChecker" level="warning" enabled="true">
+ <parameters>
+ <parameter name="regex">
+ <![CDATA[^[A-Z_]$]]>
+ </parameter>
+ </parameters>
+ </check>
+<!-- <check class="org.scalastyle.scalariform.UnderscoreImportChecker" level="warning" enabled="true" />-->
+ <check class="org.scalastyle.scalariform.LowercasePatternMatchChecker" level="warning" enabled="true" />
+ <check class="org.scalastyle.scalariform.EmptyInterpolatedStringChecker" level="warning" enabled="true" />
+ <check class="org.scalastyle.scalariform.MultipleStringLiteralsChecker" level="warning" enabled="false">
+ <parameters>
+ <parameter name="allowed">
+ <![CDATA[2]]>
+ </parameter>
+ <parameter name="ignoreRegex">
+ <![CDATA[^""$]]>
+ </parameter>
+ </parameters>
+ </check>
+ <check class="org.scalastyle.scalariform.ImportGroupingChecker" level="warning" enabled="true" />
+ <check class="org.scalastyle.scalariform.NotImplementedErrorUsage" level="warning" enabled="true" />
+ <check class="org.scalastyle.scalariform.BlockImportChecker" level="warning" enabled="false" />
+ <check class="org.scalastyle.scalariform.ProcedureDeclarationChecker" level="warning" enabled="true" />
+ <check class="org.scalastyle.scalariform.ForBraceChecker" level="warning" enabled="true" />
+ <check class="org.scalastyle.scalariform.ForLoopChecker" level="warning" enabled="true" />
+ <check class="org.scalastyle.scalariform.SpaceAfterCommentStartChecker" level="warning" enabled="true" />
+ <check class="org.scalastyle.scalariform.DisallowSpaceAfterTokenChecker" level="warning" enabled="false" />
+ <check class="org.scalastyle.scalariform.DisallowSpaceBeforeTokenChecker" level="warning" enabled="false" />
+ <check class="org.scalastyle.scalariform.EnsureSingleSpaceAfterTokenChecker" level="warning" enabled="false" />
+ <check class="org.scalastyle.scalariform.EnsureSingleSpaceBeforeTokenChecker" level="warning" enabled="false" />
+ <check class="org.scalastyle.scalariform.NonASCIICharacterChecker" level="warning" enabled="false" />
+ <check class="org.scalastyle.file.IndentationChecker" level="warning" enabled="true">
+ <parameters>
+ <parameter name="tabSize">
+ <![CDATA[4]]>
+ </parameter>
+ </parameters>
+ </check>
+ <check class="org.scalastyle.scalariform.FieldNamesChecker" level="warning" enabled="false">
+ <parameters>
+ <parameter name="regex">
+ <![CDATA[^[a-z][A-Za-z]*$]]>
+ </parameter>
+ <parameter name="objectFieldRegex">
+ <![CDATA[^[A-Z][A-Za-z]*$]]>
+ </parameter>
+ </parameters>
+ </check>
+ <check class="org.scalastyle.scalariform.TodoCommentChecker" level="warning" enabled="true">
+ <parameters>
+ <parameter name="words">
+ <![CDATA[TODO|FIXME]]>
+ </parameter>
+ </parameters>
+ </check>
+ <check class="org.scalastyle.scalariform.NamedArgumentChecker" level="warning" enabled="false">
+ <parameters>
+ <parameter name="checkString">
+ <![CDATA[false]]>
+ </parameter>
+ <parameter name="ignoreMethod">
+ <![CDATA[^set.+$]]>
+ </parameter>
+ </parameters>
+ </check>
+ <check class="org.scalastyle.scalariform.CurliesImportChecker" level="warning" enabled="false" />
+ <check class="org.scalastyle.scalariform.ThrowChecker" level="warning" enabled="false" />
+
+
+ <!-- Below are custom checks -->
+ <check enabled="true" class="org.scalastyle.scalariform.ImportOrderChecker" level="error">
+ <parameters>
+ <parameter name="groups">java,javax,scala,others</parameter>
+ <parameter name="group.java">java\..+</parameter>
+ <parameter name="group.javax">javax\..+</parameter>
+ <parameter name="group.scala">scala\..+</parameter>
+ <parameter name="group.others">.+</parameter>
+ <parameter name="maxBlankLines">0</parameter>
+ </parameters>
+ </check>
+ <check enabled="true" class="org.scalastyle.scalariform.BlockImportChecker" level="warning"/>
+ <check level="warning" class="org.scalastyle.scalariform.UnderscoreImportChecker" enabled="true">
+ <parameters>
+ <parameter name="ignoreRegex">
+ scala\.jdk\.CollectionConverters\._|io\.gatling\.core\.Predef\._|org\.apache\.ignite\.gatling\.Predef\._
+ </parameter>
+ </parameters>
+ </check>
+ <check class="org.scalastyle.scalariform.ScalaDocChecker" level="warning" enabled="true">
+ <parameters>
+ <parameter name="ignoreRegex">
+ <![CDATA[^$]]>
+ </parameter>
+ <parameter name="ignoreTokenTypes">
+ <![CDATA[]]>
+ </parameter>
+ <parameter name="ignoreOverride">
+ <![CDATA[true]]>
+ </parameter>
+ <parameter name="indentStyle">
+ <![CDATA[anydoc]]>
+ </parameter>
+ </parameters>
+ </check>
+ <check level="warning" class="org.scalastyle.scalariform.MagicNumberChecker" enabled="false"/>
+</scalastyle>
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/IgniteDsl.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/IgniteDsl.scala
new file mode 100644
index 00000000..722d1209
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/IgniteDsl.scala
@@ -0,0 +1,199 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling
+
+import io.gatling.core.Predef.{group => gatlingGroup}
+import io.gatling.core.Predef.exec
+import io.gatling.core.action.builder.ActionBuilder
+import io.gatling.core.session.Expression
+import io.gatling.core.session.Session
+import io.gatling.core.structure.ChainBuilder
+import io.gatling.core.structure.ScenarioBuilder
+import org.apache.ignite.binary.BinaryObjectBuilder
+import org.apache.ignite.client.IgniteClient
+import org.apache.ignite.gatling.api.IgniteApi
+import org.apache.ignite.gatling.builder.cache.Cache
+import org.apache.ignite.gatling.builder.ignite.Ignite
+import org.apache.ignite.gatling.builder.ignite.LambdaActionBuilder
+import org.apache.ignite.gatling.builder.transaction.Transactions
+import org.apache.ignite.gatling.check.IgniteKeyValueEntriesCheckSupport
+import org.apache.ignite.gatling.check.IgniteKeyValueMapResultCheckSupport
+import org.apache.ignite.gatling.check.IgniteSqlCheckSupport
+import org.apache.ignite.gatling.protocol.IgniteProtocol.IgniteApiSessionKey
+import org.apache.ignite.gatling.protocol.IgniteProtocolSupport
+
+/**
+ * Ignite Gatling DSL functions.
+ */
+trait IgniteDsl
+ extends IgniteProtocolSupport
+ with Ignite
+ with Transactions
+ with Cache
+ with IgniteKeyValueMapResultCheckSupport
+ with IgniteKeyValueEntriesCheckSupport
+ with IgniteSqlCheckSupport {
+
+ /**
+ * Allows the `ignite` structure DSL element to be directly created.
+ *
+ * `ignite` element allows to execute sequence of one or more comma-separated actions as follows
+ *
+ * {{{
+ * val fragment = ignite(
+ * createCache("test-cache"),
+ * put("test-cache", "#{key}", "#{value}"),
+ * get("test-cache", key = "#{key}"
+ * )
+ * }}}
+ * @param firstBuilder Chain builder foe the first action.
+ * @param chainBuilders Chain builders for subsequent actions.
+ * @return Final chain builder.
+ */
+ def ignite(firstBuilder: ChainBuilder, chainBuilders: ChainBuilder*): ChainBuilder =
+ exec(firstBuilder).exec(chainBuilders)
+
+ /**
+ * Implicit allowing ActionBuilder to be used in place of ChainBuilder.
+ *
+ * Used in the `ignite` DSL structure element in particular.
+ *
+ * @param actionBuilder ActionBuilder for single action.
+ * @return Chain builder construction chain consisting from this single action.
+ */
+ implicit def actionBuilder2ChainBuilder(actionBuilder: ActionBuilder): ChainBuilder = new ChainBuilder(List(actionBuilder))
+
+ /**
+ * Implicit conversion from lambda function accepting Ignite API and session instances to ActionBuilder.
+ *
+ * @tparam I Ignite API class.
+ * @param function Lambda function.
+ * @return LambdaActionBuilder.
+ */
+ implicit def igniteLambda2ActionBuilder[I](function: (I, Session) => Any): LambdaActionBuilder[I] =
+ LambdaActionBuilder(function)
+
+ /**
+ * Implicit conversion from lambda function accepting Ignite instance to ActionBuilder.
+ *
+ * @param function Lambda function.
+ * @return LambdaActionBuilder.
+ */
+ implicit def igniteLambda2ActionBuilder(function: org.apache.ignite.Ignite => Any): LambdaActionBuilder[org.apache.ignite.Ignite] =
+ LambdaActionBuilder((ignite: org.apache.ignite.Ignite, _: Session) => function(ignite))
+
+ /**
+ * Implicit conversion from lambda function accepting IgniteClient instance to ActionBuilder.
+ *
+ * @param function Lambda function.
+ * @return LambdaActionBuilder.
+ */
+ implicit def igniteClientLambda2ActionBuilder(function: IgniteClient => Any): LambdaActionBuilder[IgniteClient] =
+ LambdaActionBuilder((ignite: IgniteClient, _: Session) => function(ignite))
+
+ /**
+ * Implicit conversion from lambda function accepting ignite
+ * and session instances to ChainBuilder.
+ *
+ * @tparam I Ignite API class.
+ * @param function Lambda function.
+ * @return ChainBuilder.
+ */
+ implicit def igniteLambda2ChainBuilder[I](function: (I, Session) => Any): ChainBuilder =
+ new ChainBuilder(List(LambdaActionBuilder(function)))
+
+ /**
+ * Implicit conversion from lambda function accepting ignite instance to ChainBuilder.
+ *
+ * @tparam I Ignite API class.
+ * @param function Lambda function.
+ * @return ChainBuilder.
+ */
+ implicit def igniteLambda2ChainBuilder[I](function: I => Any): ChainBuilder =
+ new ChainBuilder(List(LambdaActionBuilder((ignite: I, _: Session) => function(ignite))))
+
+ /**
+ * Scenario builder allowing the `ignite` structure DSL element.
+ *
+ * @param scn Original Gatling scenario builder.
+ */
+ class IgniteScenarioBuilder(scn: ScenarioBuilder) {
+ /**
+ * Chains `ignite` structure DSL element.
+ *
+ * @param chainBuilders Chain builders for action to be executed in context of Ignite.
+ * @return itself.
+ */
+ def ignite(chainBuilders: ChainBuilder*): ScenarioBuilder = scn.exec(chainBuilders.map(b => exec(b)))
+ }
+
+ /**
+ * Adds `ignite` structure DSL element to original gatling scenario builder allowing the following syntax:
+ * {{{
+ * val scn = scenario("Ignite")
+ * .feeder(feeder)
+ * .ignite(
+ * createCache("test-cache"),
+ * put("test-cache", "#{key}", "#{value}")
+ * )
+ * }}}
+ * @param scn Original gatling scenario builder.
+ * @return Ignite scenario builder.
+ */
+ implicit def scenarioBuilder2IgniteScenarioBuilder(scn: ScenarioBuilder): IgniteScenarioBuilder = new IgniteScenarioBuilder(scn)
+
+ /**
+ * Enhanced `group` DSL structure element accepting comma-separated list of chain builders.
+ *
+ * @param name Group name.
+ * @param newActionBuilders Collection of chain builders.
+ * @return Chain builder for the actions that make up a group.
+ */
+ def group(name: Expression[String])(newActionBuilders: ChainBuilder*): ChainBuilder =
+ gatlingGroup(name)(exec(newActionBuilders))
+
+ /**
+ * Extension methods for session executing the Ignite simulation.
+ * @param session Session instance.
+ */
+ implicit class SessionEx(session: Session) {
+ /**
+ * Extracts IgniteApi instance from session.
+ *
+ * It may be needed to access Ignite functionality not exposed via the DSL. Say request
+ * the affinity information for cache.
+ *
+ * @return IgniteApi instance.
+ */
+ def igniteApi: IgniteApi = session(IgniteApiSessionKey).as[IgniteApi]
+
+ /**
+ * Requests instance of the binary object builder from the underlining IgniteApi stored in session.
+ *
+ * @param typeName Type name.
+ * @return Binary object builder instance.
+ */
+ def binaryBuilder()(typeName: String): BinaryObjectBuilder = igniteApi.binaryObjectBuilder(typeName)
+ }
+}
+
+/**
+ * Exception thrown if some invalid configuration was specified via the DSL.
+ *
+ * @param message Error message.
+ */
+class IgniteDslInvalidConfigurationException(message: String) extends RuntimeException(message)
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/Predef.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/Predef.scala
new file mode 100644
index 00000000..46b2b98d
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/Predef.scala
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling
+
+/**
+ * Ignite Gatling DSL definitions.
+ */
+object Predef extends IgniteDsl {
+ /** Fully replicated cache mode. */
+ val REPLICATED = org.apache.ignite.cache.CacheMode.REPLICATED
+ /** Partitioned cache mode */
+ val PARTITIONED = org.apache.ignite.cache.CacheMode.PARTITIONED
+
+ /** Transactional cache atomicity mode. */
+ val TRANSACTIONAL = org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL
+ /** Atomic cache atomicity mode. */
+ val ATOMIC = org.apache.ignite.cache.CacheAtomicityMode.ATOMIC
+
+ /** Pessimistic transaction concurrency control. */
+ val PESSIMISTIC = org.apache.ignite.transactions.TransactionConcurrency.PESSIMISTIC
+ /** Optimistic transaction concurrency control. */
+ val OPTIMISTIC = org.apache.ignite.transactions.TransactionConcurrency.OPTIMISTIC
+
+ /** Repeatable read transaction isolation level. */
+ val REPEATABLE_READ = org.apache.ignite.transactions.TransactionIsolation.REPEATABLE_READ
+ /** Read committed transaction isolation level. */
+ val READ_COMMITTED = org.apache.ignite.transactions.TransactionIsolation.READ_COMMITTED
+ /** Serializable read transaction isolation level. */
+ val SERIALIZABLE = org.apache.ignite.transactions.TransactionIsolation.SERIALIZABLE
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/CacheAction.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/CacheAction.scala
new file mode 100644
index 00000000..383b0198
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/CacheAction.scala
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.action
+
+import com.typesafe.scalalogging.StrictLogging
+import io.gatling.commons.validation.Failure
+import io.gatling.commons.validation.SuccessWrapper
+import io.gatling.commons.validation.Validation
+import io.gatling.core.action.Action
+import io.gatling.core.session.Session
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.gatling.api.CacheApi
+import org.apache.ignite.gatling.api.TransactionApi
+import org.apache.ignite.gatling.protocol.IgniteProtocol.ExplicitLockWasUsedSessionKey
+
+/**
+ * Base action class for cache operations.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @param actionType Action type name.
+ * @param requestName Name of the request provided via the DSL. May be empty. If so name will be generated
+ * automatically based on action type and cache name.
+ * @param ctx Gatling scenario context.
+ * @param next Next action to execute in scenario chain.
+ * @param cacheName Name of cache.
+ * @param keepBinary True if it should operate with binary objects.
+ * @param async True if async API should be used.
+ */
+abstract class CacheAction[K, V](
+ actionType: String,
+ requestName: String,
+ ctx: ScenarioContext,
+ next: Action,
+ val cacheName: String,
+ val keepBinary: Boolean,
+ val async: Boolean = false
+) extends IgniteAction(actionType, requestName, ctx, next)
+ with StrictLogging {
+
+ override val request: String = if (requestName == "") s"$name $cacheName" else requestName
+
+ /**
+ * Common parameters for cache actions.
+ *
+ * @param cacheApi Instance of CacheApi.
+ * @param transactionApi Instance of TransactionApi.
+ */
+ case class CacheActionParameters(cacheApi: CacheApi[K, V], transactionApi: Option[TransactionApi])
+
+ /**
+ * Resolves cache action parameters using session context.
+ *
+ * @param session Session.
+ * @return Instance of CacheActionParameters
+ */
+ def resolveCacheParameters(session: Session): Validation[CacheActionParameters] =
+ for {
+ IgniteActionParameters(igniteApi, transactionApi) <- resolveIgniteParameters(session)
+
+ explicitLocksWereUsed <- session(ExplicitLockWasUsedSessionKey).asOption[Boolean].success
+
+ cacheApi <- {
+ if (async && (explicitLocksWereUsed.nonEmpty || transactionApi.isDefined)) {
+ Failure("Async Ignite API can not be used in transaction context or along with the explicit locks.")
+ } else {
+ if (keepBinary) {
+ igniteApi.cache[K, V](cacheName).map(cache => cache.withKeepBinary())
+ } else {
+ igniteApi.cache[K, V](cacheName)
+ }
+ }
+ }
+ } yield CacheActionParameters(cacheApi, transactionApi)
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/IgniteAction.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/IgniteAction.scala
new file mode 100644
index 00000000..31c385fe
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/IgniteAction.scala
@@ -0,0 +1,280 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.action
+
+import io.gatling.commons.stats.KO
+import io.gatling.commons.stats.OK
+import io.gatling.commons.stats.Status
+import io.gatling.commons.util.Clock
+import io.gatling.commons.validation.Failure
+import io.gatling.commons.validation.SuccessWrapper
+import io.gatling.commons.validation.Validation
+import io.gatling.core.Predef._
+import io.gatling.core.action.Action
+import io.gatling.core.action.ChainableAction
+import io.gatling.core.check.Check
+import io.gatling.core.session.Session
+import io.gatling.core.stats.StatsEngine
+import io.gatling.core.structure.ScenarioContext
+import io.gatling.core.util.NameGen
+import org.apache.ignite.gatling.api.IgniteApi
+import org.apache.ignite.gatling.api.TransactionApi
+import org.apache.ignite.gatling.protocol.IgniteProtocol
+import org.apache.ignite.gatling.protocol.IgniteProtocol.IgniteApiSessionKey
+import org.apache.ignite.gatling.protocol.IgniteProtocol.TransactionApiSessionKey
+
+/**
+ * Base class for all Ignite actions.
+ *
+ * @param actionType Action type name.
+ * @param requestName Name of the request provided via the DSL. May be empty. If so it will be generated from action type name.
+ * @param ctx Gatling scenario context.
+ * @param next Next action to execute in scenario chain.
+ */
+abstract class IgniteAction(val actionType: String, val requestName: String, val ctx: ScenarioContext, val next: Action)
+ extends ChainableAction
+ with NameGen {
+ /** @return Action name. */
+ val name: String = genName(actionType)
+
+ /** @return Default request name if none was provided via the DSL. */
+ val request: String = if (requestName == "") name else requestName
+
+ /** Clock used to measure time the action takes. */
+ val clock: Clock = {
+ val components = ctx.protocolComponentsRegistry.components(IgniteProtocol.IgniteProtocolKey)
+
+ components.clock.getOrElse(
+ components.coreComponents.clock
+ )
+ }
+
+ /** Statistics engine */
+ val statsEngine: StatsEngine = ctx.protocolComponentsRegistry.components(IgniteProtocol.IgniteProtocolKey).coreComponents.statsEngine
+
+ /** Ignite protocol */
+ val protocol: IgniteProtocol = ctx.protocolComponentsRegistry.components(IgniteProtocol.IgniteProtocolKey).igniteProtocol
+
+ /**
+ * Logs results of action execution and starts the next action in the chain.
+ *
+ * @param session Session.
+ * @param requestName Name of the request.
+ * @param sent Timestamp of the request processing start.
+ * @param received Timestamp of the request processing finish.
+ * @param status Status of the executed request.
+ * @param responseCode Optional string representation of response code in case action failed.
+ * @param message Optional error message in case action failed.
+ */
+ protected def logAndExecuteNext(
+ session: Session,
+ requestName: String,
+ sent: Long,
+ received: Long,
+ status: Status,
+ responseCode: Option[String],
+ message: Option[String]
+ ): Unit = {
+ statsEngine.logResponse(
+ session.scenario,
+ session.groups,
+ requestName,
+ sent,
+ received,
+ status,
+ responseCode,
+ message
+ )
+
+ next ! session.logGroupRequestTimings(sent, received)
+ }
+
+ /**
+ * Executes an action in context of Session. Logs crash if session context is not valid.
+ *
+ * @param session Session.
+ * @param f Function executing an action.
+ */
+ protected def withSessionCheck(session: Session)(f: => Validation[Unit]): Unit = {
+ logger.debug(s"session user id: #${session.userId}, $name")
+ f.onFailure { errorMessage =>
+ logger.error(
+ s"Error in ignite action during session check, user id: #${session.userId}, request name: $request: $errorMessage"
+ )
+
+ statsEngine.logCrash(session.scenario, session.groups, request, errorMessage)
+
+ statsEngine.logResponse(
+ session.scenario,
+ session.groups,
+ request,
+ clock.nowMillis,
+ clock.nowMillis,
+ KO,
+ Some("CRASH"),
+ Some(errorMessage)
+ )
+
+ stopSimulation(errorMessage, session.markAsFailed)
+ }
+ }
+
+ private def stopSimulation(errorMessage: String, session: Session): Unit =
+ stopInjector(errorMessage).actionBuilders.head.build(
+ ctx,
+ exitHere.actionBuilders.head.build(ctx, next)
+ ) ! session
+
+ /**
+ * Common parameters for ignite actions.
+ *
+ * @param igniteApi Instance of IgniteApi.
+ * @param transactionApi Instance of TransactionApi. Present in session context if Ignite transaction was started.
+ */
+ case class IgniteActionParameters(igniteApi: IgniteApi, transactionApi: Option[TransactionApi])
+
+ /**
+ * Resolves ignite action parameters using session context.
+ *
+ * @param session Session.
+ * @return Instance of IgniteActionParameters.
+ */
+ def resolveIgniteParameters(session: Session): Validation[IgniteActionParameters] =
+ for {
+ igniteApi <- session(IgniteApiSessionKey).validate[IgniteApi]
+
+ transactionApi <- session(TransactionApiSessionKey).asOption[TransactionApi].success
+ } yield IgniteActionParameters(igniteApi, transactionApi)
+
+ /**
+ * Calls function from the Ignite API,
+ * performs checks against the result,
+ * records outcome and execution time and
+ * invokes next action in the chain.
+ *
+ * In case of successful execution of function calls the lambda provided to update the session state.
+ *
+ * @tparam R Type of the result.
+ * @param func Function to execute (some function from the Ignite API).
+ * @param session Session.
+ * @param checks List of checks to perform (may be empty).
+ * @param updateSession Function to be called to update session on successful function execution. Keep
+ * session unchanged if absent.
+ * @param s Session.
+ * @param r Result of the operation.
+ */
+ def call[R](
+ func: (R => Unit, Throwable => Unit) => Unit,
+ session: Session,
+ checks: Seq[Check[R]] = Seq.empty,
+ updateSession: (Session, Option[R]) => Session = (s: Session, r: Option[R]) => s
+ ): Unit = {
+ val startTime = clock.nowMillis
+
+ func(
+ result => {
+ val finishTime = clock.nowMillis
+
+ logger.debug(s"session user id: #${session.userId}, after $name, result $result")
+
+ processResult(
+ result,
+ startTime,
+ finishTime,
+ request,
+ session,
+ checks,
+ updateSession
+ )
+ },
+ ex => {
+ logger.debug(s"Exception in ignite actions, user id: #${session.userId}, request name: $request", ex)
+
+ handleError(
+ ex,
+ startTime,
+ request,
+ session
+ )
+ }
+ )
+ }
+
+ private def processResult[R](
+ result: R,
+ startTime: Long,
+ finishTime: Long,
+ request: String,
+ session: Session,
+ checks: Seq[Check[R]] = Seq.empty,
+ updateSession: (Session, Option[R]) => Session
+ ): Unit = {
+ val (checkedSession, error) = Check.check(result, session, checks.toList)
+
+ error match {
+ case Some(Failure(errorMessage)) =>
+ logAndExecuteNext(
+ updateSession(checkedSession.markAsFailed, Some(result)),
+ request,
+ startTime,
+ finishTime,
+ KO,
+ Some("Check ERROR"),
+ Some(errorMessage)
+ )
+
+ case None =>
+ logAndExecuteNext(
+ updateSession(checkedSession, Some(result)),
+ request,
+ startTime,
+ finishTime,
+ OK,
+ None,
+ None
+ )
+ }
+ }
+
+ private def handleError(
+ ex: Throwable,
+ startTime: Long,
+ request: String,
+ session: Session
+ ): Unit = {
+ session(TransactionApiSessionKey).asOption[TransactionApi].foreach {
+ _.close(_ => (), _ => ())
+ }
+
+ logAndExecuteNext(
+ session.markAsFailed,
+ request,
+ startTime,
+ clock.nowMillis,
+ KO,
+ Some("ERROR"),
+ Some(strip(ex.getMessage, 120))
+ )
+ }
+
+ private def strip(string: String, length: Int): String =
+ if (string.length <= length) {
+ string
+ } else {
+ string.take(length) + " ..."
+ }
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/ArgumentsResolveSupport.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/ArgumentsResolveSupport.scala
new file mode 100644
index 00000000..fd137cd5
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/ArgumentsResolveSupport.scala
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.action.cache
+
+import io.gatling.commons.validation.SuccessWrapper
+import io.gatling.commons.validation.Validation
+import io.gatling.core.session.Expression
+import io.gatling.core.session.Session
+
+/**
+ * Helper to resolve arguments represented as a sequence of expressions.
+ */
+trait ArgumentsResolveSupport {
+ /**
+ * Resolves sequence of arguments using the session context.
+ *
+ * @param session Session.
+ * @param arguments List of arguments to resolve.
+ * @return List of the resolved arguments.
+ */
+ def resolveArguments(session: Session, arguments: Seq[Expression[Any]]): Validation[List[Any]] =
+ arguments
+ .foldLeft(List[Any]().success) { (acc, v) =>
+ acc.flatMap(acc => v(session).map(v => v :: acc))
+ }
+ .map(l => l.reverse)
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CacheGetAction.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CacheGetAction.scala
new file mode 100644
index 00000000..03ba9df5
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CacheGetAction.scala
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.action.cache
+
+import io.gatling.core.action.Action
+import io.gatling.core.session.Expression
+import io.gatling.core.session.Session
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.gatling.Predef.IgniteCheck
+import org.apache.ignite.gatling.action.CacheAction
+
+/**
+ * Action for the get Ignite operation.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @param requestName Name of the request.
+ * @param cacheName Name of cache.
+ * @param key Cache entry key.
+ * @param keepBinary True if it should operate with binary objects.
+ * @param async True if async API should be used.
+ * @param checks Collection of checks to perform against the operation result.
+ * @param next Next action from chain to invoke upon this one completion.
+ * @param ctx Scenario context.
+ */
+class CacheGetAction[K, V](
+ requestName: String,
+ cacheName: String,
+ key: Expression[K],
+ keepBinary: Boolean,
+ async: Boolean,
+ checks: Seq[IgniteCheck[K, V]],
+ next: Action,
+ ctx: ScenarioContext
+) extends CacheAction[K, V]("get", requestName, ctx, next, cacheName, keepBinary, async) {
+
+ override protected def execute(session: Session): Unit = withSessionCheck(session) {
+ for {
+ CacheActionParameters(cacheApi, _) <- resolveCacheParameters(session)
+
+ resolvedKey <- key(session)
+ } yield {
+ logger.debug(s"session user id: #${session.userId}, before $request")
+
+ val func = if (async) cacheApi.getAsync _ else cacheApi.get _
+
+ call(func(resolvedKey), session, checks)
+ }
+ }
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CacheGetAllAction.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CacheGetAllAction.scala
new file mode 100644
index 00000000..0c02e41e
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CacheGetAllAction.scala
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.action.cache
+
+import scala.collection.SortedSet
+
+import io.gatling.core.action.Action
+import io.gatling.core.session.Expression
+import io.gatling.core.session.Session
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.gatling.Predef.IgniteCheck
+import org.apache.ignite.gatling.action.CacheAction
+
+/**
+ * Action for the getAll Ignite operation.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @param requestName Name of the request.
+ * @param cacheName Name of cache.
+ * @param keys Collection of cache entry keys.
+ * @param keepBinary True if it should operate with binary objects.
+ * @param async True if async API should be used.
+ * @param checks Collection of checks to perform against the operation result.
+ * @param next Next action from chain to invoke upon this one completion.
+ * @param ctx Scenario context.
+ */
+class CacheGetAllAction[K, V](
+ requestName: String,
+ cacheName: String,
+ keys: Expression[SortedSet[K]],
+ keepBinary: Boolean,
+ async: Boolean,
+ checks: Seq[IgniteCheck[K, V]],
+ next: Action,
+ ctx: ScenarioContext
+) extends CacheAction[K, V]("getAll", requestName, ctx, next, cacheName, keepBinary, async) {
+
+ override protected def execute(session: Session): Unit = withSessionCheck(session) {
+ for {
+ resolvedKeys <- keys(session)
+
+ CacheActionParameters(cacheApi, _) <- resolveCacheParameters(session)
+ } yield {
+ logger.debug(s"session user id: #${session.userId}, before $request")
+
+ val func = if (async) cacheApi.getAllAsync _ else cacheApi.getAll _
+
+ call(func(resolvedKeys), session, checks)
+ }
+ }
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CacheGetAndPutAction.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CacheGetAndPutAction.scala
new file mode 100644
index 00000000..1fd7a057
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CacheGetAndPutAction.scala
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.action.cache
+
+import io.gatling.core.action.Action
+import io.gatling.core.session.Expression
+import io.gatling.core.session.Session
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.gatling.Predef.IgniteCheck
+import org.apache.ignite.gatling.action.CacheAction
+
+/**
+ * Action for the getAndPut Ignite operation.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @param requestName Name of the request.
+ * @param cacheName Name of cache.
+ * @param key Cache entry key.
+ * @param value Cache entry value.
+ * @param keepBinary True if it should operate with binary objects.
+ * @param async True if async API should be used.
+ * @param checks Collection of checks to perform against the operation result.
+ * @param next Next action from chain to invoke upon this one completion.
+ * @param ctx Scenario context.
+ */
+class CacheGetAndPutAction[K, V](
+ requestName: String,
+ cacheName: String,
+ key: Expression[K],
+ value: Expression[V],
+ keepBinary: Boolean,
+ async: Boolean,
+ checks: Seq[IgniteCheck[K, V]],
+ next: Action,
+ ctx: ScenarioContext
+) extends CacheAction[K, V]("getAndPut", requestName, ctx, next, cacheName, keepBinary, async) {
+
+ override protected def execute(session: Session): Unit = withSessionCheck(session) {
+ for {
+ CacheActionParameters(cacheApi, _) <- resolveCacheParameters(session)
+
+ resolvedKey <- key(session)
+
+ resolvedValue <- value(session)
+ } yield {
+ logger.debug(s"session user id: #${session.userId}, before $request")
+
+ val func = if (async) cacheApi.getAndPutAsync _ else cacheApi.getAndPut _
+
+ call(func(resolvedKey, resolvedValue), session, checks)
+ }
+ }
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CacheGetAndRemoveAction.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CacheGetAndRemoveAction.scala
new file mode 100644
index 00000000..4f297f07
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CacheGetAndRemoveAction.scala
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.action.cache
+
+import io.gatling.core.action.Action
+import io.gatling.core.session.Expression
+import io.gatling.core.session.Session
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.gatling.Predef.IgniteCheck
+import org.apache.ignite.gatling.action.CacheAction
+
+/**
+ * Action for the getAndRemove Ignite operation.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @param requestName Name of the request.
+ * @param cacheName Name of cache.
+ * @param key Cache entry key.
+ * @param keepBinary True if it should operate with binary objects.
+ * @param async True if async API should be used.
+ * @param checks Collection of checks to perform against the operation result.
+ * @param next Next action from chain to invoke upon this one completion.
+ * @param ctx Scenario context.
+ */
+class CacheGetAndRemoveAction[K, V](
+ requestName: String,
+ cacheName: String,
+ key: Expression[K],
+ keepBinary: Boolean,
+ async: Boolean,
+ checks: Seq[IgniteCheck[K, V]],
+ next: Action,
+ ctx: ScenarioContext
+) extends CacheAction[K, V]("getAndRemove", requestName, ctx, next, cacheName, keepBinary, async) {
+
+ override protected def execute(session: Session): Unit = withSessionCheck(session) {
+ for {
+ CacheActionParameters(cacheApi, _) <- resolveCacheParameters(session)
+
+ resolvedKey <- key(session)
+ } yield {
+ logger.debug(s"session user id: #${session.userId}, before $request")
+
+ val func = if (async) cacheApi.getAndRemoveAsync _ else cacheApi.getAndRemove _
+
+ call(func(resolvedKey), session, checks)
+ }
+ }
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CacheInvokeAction.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CacheInvokeAction.scala
new file mode 100644
index 00000000..218de94c
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CacheInvokeAction.scala
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.action.cache
+
+import io.gatling.core.action.Action
+import io.gatling.core.session.Expression
+import io.gatling.core.session.Session
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.cache.CacheEntryProcessor
+import org.apache.ignite.gatling.Predef.IgniteCheck
+import org.apache.ignite.gatling.action.CacheAction
+
+/**
+ * Action for the invoke Ignite operation.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @tparam T Type of the operation result.
+ * @param requestName Name of the request.
+ * @param cacheName Name of cache.
+ * @param key Cache entry key.
+ * @param entryProcessor Instance of CacheEntryProcessor.
+ * @param arguments Additional arguments to pass to the entry processor.
+ * @param keepBinary True if it should operate with binary objects.
+ * @param async True if async API should be used.
+ * @param checks Collection of checks to perform against the operation result.
+ * @param next Next action from chain to invoke upon this one completion.
+ * @param ctx Scenario context.
+ */
+class CacheInvokeAction[K, V, T](
+ requestName: String,
+ cacheName: String,
+ key: Expression[K],
+ entryProcessor: CacheEntryProcessor[K, V, T],
+ arguments: Seq[Expression[Any]],
+ keepBinary: Boolean,
+ async: Boolean,
+ checks: Seq[IgniteCheck[K, T]],
+ next: Action,
+ ctx: ScenarioContext
+) extends CacheAction[K, V]("invoke", requestName, ctx, next, cacheName, keepBinary, async)
+ with ArgumentsResolveSupport {
+
+ override protected def execute(session: Session): Unit = withSessionCheck(session) {
+ for {
+ CacheActionParameters(cacheApi, _) <- resolveCacheParameters(session)
+
+ resolvedKey <- key(session)
+
+ resolvedArguments <- resolveArguments(session, arguments)
+ } yield {
+ logger.debug(s"session user id: #${session.userId}, before $request")
+
+ val func = if (async) cacheApi.invokeAsync[T] _ else cacheApi.invoke[T] _
+
+ call(func(resolvedKey, entryProcessor, resolvedArguments), session, checks)
+ }
+ }
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CacheInvokeAllAction.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CacheInvokeAllAction.scala
new file mode 100644
index 00000000..cfe6c511
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CacheInvokeAllAction.scala
@@ -0,0 +1,159 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.action.cache
+
+import javax.cache.processor.EntryProcessorResult
+
+import scala.collection.SortedMap
+import scala.collection.SortedSet
+
+import io.gatling.core.action.Action
+import io.gatling.core.session.Expression
+import io.gatling.core.session.Session
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.cache.CacheEntryProcessor
+import org.apache.ignite.gatling.Predef.IgniteCheck
+import org.apache.ignite.gatling.action.CacheAction
+import org.apache.ignite.gatling.api.CacheApi
+
+/**
+ * Common functions for invokeAll actions of two flavours.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @tparam T Type of the operation result.
+ */
+trait CacheInvokeAllAction[K, V, T] extends ArgumentsResolveSupport {
+ this: CacheAction[K, V] =>
+
+ /** Additional arguments to pass to the entry processor. */
+ val arguments: Seq[Expression[Any]]
+
+ /** Collection of checks to perform against the operation result. */
+ val checks: Seq[IgniteCheck[K, EntryProcessorResult[T]]]
+
+ /**
+ * Method executed when the Action received a Session message.
+ * @param session Session
+ * @param cacheApi Instance of CacheApi.
+ * @param resolvedMap Resolved map from cache entry key to entry processor instance.
+ * @param resolvedArguments Resolved entry processor arguments.
+ */
+ def execute(
+ session: Session,
+ cacheApi: CacheApi[K, V],
+ resolvedMap: SortedMap[K, CacheEntryProcessor[K, V, T]],
+ resolvedArguments: List[Any]
+ ): Unit = {
+
+ logger.debug(s"session user id: #${session.userId}, before $request")
+
+ val func = if (async) cacheApi.invokeAllAsync[T] _ else cacheApi.invokeAll[T] _
+
+ call(func(resolvedMap, resolvedArguments), session, checks)
+ }
+}
+
+/**
+ * Action for the invokeAll Ignite operation in case its own cache processor instance
+ * was provided for each cache entry.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @tparam T Type of the operation result.
+ * @param requestName Name of the request.
+ * @param cacheName Name of cache.
+ * @param map Map from cache entry key to CacheEntryProcessor to invoke for this particular entry.
+ * @param arguments Additional arguments to pass to the entry processor.
+ * @param keepBinary True if it should operate with binary objects.
+ * @param async True if async API should be used.
+ * @param checks Collection of checks to perform against the operation result.
+ * @param next Next action from chain to invoke upon this one completion.
+ * @param ctx Scenario context.
+ */
+class CacheInvokeAllMapAction[K, V, T](
+ requestName: String,
+ cacheName: String,
+ map: Expression[SortedMap[K, CacheEntryProcessor[K, V, T]]],
+ val arguments: Seq[Expression[Any]],
+ keepBinary: Boolean,
+ async: Boolean,
+ val checks: Seq[IgniteCheck[K, EntryProcessorResult[T]]],
+ next: Action,
+ ctx: ScenarioContext
+) extends CacheAction[K, V]("invokeAll", requestName, ctx, next, cacheName, keepBinary, async)
+ with CacheInvokeAllAction[K, V, T] {
+
+ override protected def execute(session: Session): Unit = withSessionCheck(session) {
+ for {
+ CacheActionParameters(cacheApi, _) <- resolveCacheParameters(session)
+
+ resolvedMap <- map(session)
+
+ resolvedArguments <- resolveArguments(session, arguments)
+ } yield execute(session, cacheApi, resolvedMap, resolvedArguments)
+ }
+}
+
+/**
+ * Action for the invokeAll Ignite operation in case a single cache processor should
+ * be executed for all cache entries.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @tparam T Type of the operation result.
+ * @param requestName Name of the request.
+ * @param cacheName Name of cache.
+ * @param keys Collection of cache entry keys.
+ * @param processor Instance of CacheEntryProcessor.
+ * @param arguments Additional arguments to pass to the entry processor.
+ * @param keepBinary True if it should operate with binary objects.
+ * @param async True if async API should be used.
+ * @param checks Collection of checks to perform against the operation result.
+ * @param next Next action from chain to invoke upon this one completion.
+ * @param ctx Scenario context.
+ */
+class CacheInvokeAllSingleProcessorAction[K, V, T](
+ requestName: String,
+ cacheName: String,
+ keys: Expression[SortedSet[K]],
+ processor: CacheEntryProcessor[K, V, T],
+ val arguments: Seq[Expression[Any]],
+ keepBinary: Boolean,
+ async: Boolean,
+ val checks: Seq[IgniteCheck[K, EntryProcessorResult[T]]],
+ next: Action,
+ ctx: ScenarioContext
+) extends CacheAction[K, V]("invokeAll", requestName, ctx, next, cacheName, keepBinary, async)
+ with CacheInvokeAllAction[K, V, T] {
+
+ override protected def execute(session: Session): Unit = withSessionCheck(session) {
+ for {
+ CacheActionParameters(cacheApi, _) <- resolveCacheParameters(session)
+
+ resolvedKeys <- keys(session)
+
+ resolvedArguments <- resolveArguments(session, arguments)
+ } yield execute(session, cacheApi, toSortedMap(resolvedKeys), resolvedArguments)
+ }
+
+ private def toSortedMap(resolvedKeys: SortedSet[K]): SortedMap[K, CacheEntryProcessor[K, V, T]] = {
+ implicit val ordering: Ordering[K] = resolvedKeys.ordering
+
+ SortedMap.from(resolvedKeys.unsorted.map(k => (k, processor)))
+ }
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CacheLockAction.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CacheLockAction.scala
new file mode 100644
index 00000000..f3533577
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CacheLockAction.scala
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.action.cache
+
+import java.util.concurrent.locks.Lock
+
+import io.gatling.core.action.Action
+import io.gatling.core.session.Expression
+import io.gatling.core.session.Session
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.gatling.Predef.IgniteCheck
+import org.apache.ignite.gatling.action.CacheAction
+import org.apache.ignite.gatling.protocol.IgniteProtocol.ExplicitLockWasUsedSessionKey
+
+/**
+ * Action for the lock cache entry Ignite operation.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @param requestName Name of the request.
+ * @param cacheName Name of cache.
+ * @param key Cache entry key.
+ * @param keepBinary True if it should operate with binary objects.
+ * @param checks Collection of checks to perform against the operation result.
+ * @param next Next action from chain to invoke upon this one completion.
+ * @param ctx Scenario context.
+ */
+class CacheLockAction[K, V](
+ requestName: String,
+ cacheName: String,
+ key: Expression[K],
+ keepBinary: Boolean,
+ checks: Seq[IgniteCheck[K, Lock]],
+ next: Action,
+ ctx: ScenarioContext
+) extends CacheAction[K, V]("lock", requestName, ctx, next, cacheName, keepBinary) {
+
+ override protected def execute(session: Session): Unit = withSessionCheck(session) {
+ for {
+ CacheActionParameters(cacheApi, _) <- resolveCacheParameters(session)
+
+ resolvedKey <- key(session)
+ } yield {
+ logger.debug(s"session user id: #${session.userId}, before $request")
+
+ val func = (s: Map[K, Lock] => Unit, f: Throwable => Unit) =>
+ cacheApi.lock(resolvedKey)(
+ v => s(Map(resolvedKey -> v)),
+ ex => f(ex)
+ )
+
+ call(
+ func,
+ session,
+ checks,
+ updateSession = (session, _: Option[Map[K, Lock]]) => session.set(ExplicitLockWasUsedSessionKey, true)
+ )
+ }
+ }
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CachePutAction.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CachePutAction.scala
new file mode 100644
index 00000000..190f9590
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CachePutAction.scala
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.action.cache
+
+import io.gatling.core.action.Action
+import io.gatling.core.session.Expression
+import io.gatling.core.session.Session
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.gatling.action.CacheAction
+
+/**
+ * Action for the put Ignite operation.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @param requestName Name of the request.
+ * @param cacheName Name of cache.
+ * @param pair Tuple for cache entry key and value.
+ * @param keepBinary True if it should operate with binary objects.
+ * @param async True if async API should be used.
+ * @param next Next action from chain to invoke upon this one completion.
+ * @param ctx Scenario context.
+ */
+class CachePutAction[K, V](
+ requestName: String,
+ cacheName: String,
+ pair: Expression[(K, V)],
+ keepBinary: Boolean,
+ async: Boolean,
+ next: Action,
+ ctx: ScenarioContext
+) extends CacheAction[K, V]("put", requestName, ctx, next, cacheName, keepBinary, async) {
+
+ override protected def execute(session: Session): Unit = withSessionCheck(session) {
+ for {
+ (resolvedKey, resolvedValue) <- pair(session)
+
+ CacheActionParameters(cacheApi, _) <- resolveCacheParameters(session)
+ } yield {
+ logger.debug(s"session user id: #${session.userId}, before $request")
+
+ val func = if (async) cacheApi.putAsync _ else cacheApi.put _
+
+ super.call(func(resolvedKey, resolvedValue), session)
+ }
+ }
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CachePutAllAction.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CachePutAllAction.scala
new file mode 100644
index 00000000..aa6568a9
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CachePutAllAction.scala
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.action.cache
+
+import scala.collection.SortedMap
+
+import io.gatling.core.action.Action
+import io.gatling.core.session.Expression
+import io.gatling.core.session.Session
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.gatling.action.CacheAction
+
+/**
+ * Action for the putAll Ignite operation.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @param requestName Name of the request.
+ * @param cacheName Name of cache.
+ * @param map Collection of cache entry keys and values.
+ * @param keepBinary True if it should operate with binary objects.
+ * @param async True if async API should be used.
+ * @param next Next action from chain to invoke upon this one completion.
+ * @param ctx Scenario context.
+ */
+class CachePutAllAction[K, V](
+ requestName: String,
+ cacheName: String,
+ map: Expression[SortedMap[K, V]],
+ keepBinary: Boolean,
+ async: Boolean,
+ next: Action,
+ ctx: ScenarioContext
+) extends CacheAction[K, V]("putAll", requestName, ctx, next, cacheName, keepBinary, async) {
+
+ override protected def execute(session: Session): Unit = withSessionCheck(session) {
+ for {
+ resolvedMap <- map(session)
+
+ CacheActionParameters(cacheApi, _) <- resolveCacheParameters(session)
+ } yield {
+ logger.debug(s"session user id: #${session.userId}, before $request")
+
+ val func = if (async) cacheApi.putAllAsync _ else cacheApi.putAll _
+
+ call(func(resolvedMap), session)
+ }
+ }
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CacheRemoveAction.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CacheRemoveAction.scala
new file mode 100644
index 00000000..263e69ef
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CacheRemoveAction.scala
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.action.cache
+
+import io.gatling.core.action.Action
+import io.gatling.core.session.Expression
+import io.gatling.core.session.Session
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.gatling.action.CacheAction
+
+/**
+ * Action for the get Ignite operation.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @param requestName Name of the request.
+ * @param cacheName Name of cache.
+ * @param key Cache entry key.
+ * @param keepBinary True if it should operate with binary objects.
+ * @param async True if async API should be used.
+ * @param next Next action from chain to invoke upon this one completion.
+ * @param ctx Scenario context.
+ */
+class CacheRemoveAction[K, V](
+ requestName: String,
+ cacheName: String,
+ key: Expression[K],
+ keepBinary: Boolean,
+ async: Boolean,
+ next: Action,
+ ctx: ScenarioContext
+) extends CacheAction[K, V]("remove", requestName, ctx, next, cacheName, keepBinary, async) {
+
+ override protected def execute(session: Session): Unit = withSessionCheck(session) {
+ for {
+ CacheActionParameters(cacheApi, _) <- resolveCacheParameters(session)
+
+ resolvedKey <- key(session)
+ } yield {
+ logger.debug(s"session user id: #${session.userId}, before $request")
+
+ val func = if (async) cacheApi.removeAsync _ else cacheApi.remove _
+
+ call(func(resolvedKey), session)
+ }
+ }
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CacheRemoveAllAction.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CacheRemoveAllAction.scala
new file mode 100644
index 00000000..c17a7d33
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CacheRemoveAllAction.scala
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.action.cache
+
+import scala.collection.SortedSet
+
+import io.gatling.core.action.Action
+import io.gatling.core.session.Expression
+import io.gatling.core.session.Session
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.gatling.action.CacheAction
+
+/**
+ * Action for the removeAll Ignite operation.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @param requestName Name of the request.
+ * @param cacheName Name of cache.
+ * @param keys Collection of cache entry keys.
+ * @param keepBinary True if it should operate with binary objects.
+ * @param async True if async API should be used.
+ * @param next Next action from chain to invoke upon this one completion.
+ * @param ctx Scenario context.
+ */
+class CacheRemoveAllAction[K, V](
+ requestName: String,
+ cacheName: String,
+ keys: Expression[SortedSet[K]],
+ keepBinary: Boolean,
+ async: Boolean,
+ next: Action,
+ ctx: ScenarioContext
+) extends CacheAction[K, V]("removeAll", requestName, ctx, next, cacheName, keepBinary, async) {
+
+ override protected def execute(session: Session): Unit = withSessionCheck(session) {
+ for {
+ resolvedKeys <- keys(session)
+
+ CacheActionParameters(cacheApi, _) <- resolveCacheParameters(session)
+ } yield {
+ logger.debug(s"session user id: #${session.userId}, before $request")
+
+ val func = if (async) cacheApi.removeAllAsync _ else cacheApi.removeAll _
+
+ call(func(resolvedKeys), session)
+ }
+ }
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CacheSqlAction.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CacheSqlAction.scala
new file mode 100644
index 00000000..c7fcb49b
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CacheSqlAction.scala
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.action.cache
+
+import io.gatling.core.action.Action
+import io.gatling.core.session.Expression
+import io.gatling.core.session.Session
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.cache.query.SqlFieldsQuery
+import org.apache.ignite.gatling.Predef.SqlCheck
+import org.apache.ignite.gatling.action.CacheAction
+
+/**
+ * Action for the SQL query Ignite operation.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @param requestName Name of the request.
+ * @param cacheName Name of cache.
+ * @param sql SQL query.
+ * @param arguments Collection of positional arguments for SQL query (may be empty).
+ * @param partitions List of cache partitions to use for query (may be empty).
+ * @param keepBinary True if it should operate with binary objects.
+ * @param checks Collection of checks to perform against the operation result.
+ * @param next Next action from chain to invoke upon this one completion.
+ * @param ctx Scenario context.
+ */
+class CacheSqlAction[K, V](
+ requestName: String,
+ cacheName: String,
+ sql: Expression[String],
+ arguments: Seq[Expression[Any]],
+ partitions: Expression[List[Int]],
+ keepBinary: Boolean,
+ checks: Seq[SqlCheck],
+ next: Action,
+ ctx: ScenarioContext
+) extends CacheAction[K, V]("sql", requestName, ctx, next, cacheName, keepBinary)
+ with ArgumentsResolveSupport {
+
+ override protected def execute(session: Session): Unit = withSessionCheck(session) {
+ for {
+ CacheActionParameters(cacheApi, _) <- resolveCacheParameters(session)
+
+ resolvedSql <- sql(session)
+
+ resolvedArgs <- resolveArguments(session, arguments)
+
+ resolvedPartitions <- partitions(session)
+ } yield {
+ logger.debug(s"session user id: #${session.userId}, before $request")
+
+ val query: SqlFieldsQuery = new SqlFieldsQuery(resolvedSql)
+
+ if (resolvedArgs.nonEmpty) {
+ query.setArgs(resolvedArgs: _*)
+ }
+
+ if (resolvedPartitions.nonEmpty) {
+ query.setPartitions(resolvedPartitions: _*)
+ }
+
+ query.setSchema("PUBLIC")
+
+ val func = cacheApi.sql(query) _
+
+ call(func, session, checks)
+ }
+ }
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CacheUnlockAction.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CacheUnlockAction.scala
new file mode 100644
index 00000000..ceda9a2f
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/cache/CacheUnlockAction.scala
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.action.cache
+
+import java.util.concurrent.locks.Lock
+
+import io.gatling.core.action.Action
+import io.gatling.core.session.Expression
+import io.gatling.core.session.Session
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.gatling.action.CacheAction
+
+/**
+ * Action for the unlock cache entry Ignite operation.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @param requestName Name of the request.
+ * @param cacheName Name of cache.
+ * @param lock Lock object.
+ * @param keepBinary True if it should operate with binary objects.
+ * @param next Next action from chain to invoke upon this one completion.
+ * @param ctx Scenario context.
+ */
+class CacheUnlockAction[K, V](
+ requestName: String,
+ cacheName: String,
+ lock: Expression[Lock],
+ keepBinary: Boolean,
+ next: Action,
+ ctx: ScenarioContext
+) extends CacheAction[K, V]("unlock", requestName, ctx, next, cacheName, keepBinary) {
+
+ override protected def execute(session: Session): Unit = withSessionCheck(session) {
+ for {
+ CacheActionParameters(cacheApi, _) <- resolveCacheParameters(session)
+
+ resolvedLock <- lock(session)
+ } yield {
+ logger.debug(s"session user id: #${session.userId}, before $request")
+
+ val func = cacheApi.unlock(resolvedLock) _
+
+ call(func, session)
+ }
+ }
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/ignite/CloseClientAction.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/ignite/CloseClientAction.scala
new file mode 100644
index 00000000..7e1bd9f8
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/ignite/CloseClientAction.scala
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.action.ignite
+
+import io.gatling.core.action.Action
+import io.gatling.core.session.Session
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.gatling.action.IgniteAction
+import org.apache.ignite.gatling.protocol.IgniteClientConfigurationCfg
+import org.apache.ignite.gatling.protocol.IgniteConfigurationCfg
+import org.apache.ignite.gatling.protocol.IgniteProtocol.IgniteApiSessionKey
+
+/**
+ * Action for the close Ignite API operation (either closes the thin client or stops the node working in client mode).
+ *
+ * @param requestName Name of the request.
+ * @param next Next action from chain to invoke upon this one completion.
+ * @param ctx Scenario context.
+ */
+class CloseClientAction(requestName: String, next: Action, ctx: ScenarioContext)
+ extends IgniteAction("close", requestName, ctx, next) {
+
+ override protected def execute(session: Session): Unit = withSessionCheck(session) {
+ val noOp: (Unit => Unit, Throwable => Unit) => Unit = (s, _) => s.apply(())
+
+ for {
+ IgniteActionParameters(igniteApi, _) <- resolveIgniteParameters(session)
+ } yield {
+ logger.debug(s"session user id: #${session.userId}, before $requestName")
+
+ val func = protocol.cfg match {
+ case IgniteClientConfigurationCfg(_) | IgniteConfigurationCfg(_) if protocol.explicitClientStart =>
+ igniteApi.close() _
+
+ case _ => noOp
+ }
+
+ call(func, session, updateSession = (session, _: Option[Unit]) => session.remove(IgniteApiSessionKey))
+ }
+ }
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/ignite/GetOrCreateCacheAction.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/ignite/GetOrCreateCacheAction.scala
new file mode 100644
index 00000000..725d9b6e
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/ignite/GetOrCreateCacheAction.scala
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.action.ignite
+
+import io.gatling.core.action.Action
+import io.gatling.core.session.Session
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.configuration.CacheConfiguration
+import org.apache.ignite.gatling.action.IgniteAction
+import org.apache.ignite.gatling.api.CacheApi
+import org.apache.ignite.gatling.api.IgniteApi
+import org.apache.ignite.gatling.builder.ignite.Configuration
+import org.apache.ignite.gatling.builder.ignite.SimpleCacheConfiguration
+import org.apache.ignite.gatling.builder.ignite.ThickConfiguration
+import org.apache.ignite.gatling.builder.ignite.ThinConfiguration
+
+/**
+ * Action for cache create Ignite operation.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @param requestName Name of the request provided via the DSL. May be empty.
+ * @param cacheName Name of the cache.
+ * @param config Abstract cache configuration.
+ * @param next Next action from chain to invoke upon this one completion.
+ * @param ctx Scenario context.
+ */
+class GetOrCreateCacheAction[K, V](
+ requestName: String,
+ cacheName: String,
+ config: Configuration,
+ next: Action,
+ ctx: ScenarioContext
+) extends IgniteAction("getOrCreateCache", requestName, ctx, next) {
+
+ override val request: String = if (requestName == "") s"$name $cacheName" else requestName
+
+ override protected def execute(session: Session): Unit = withSessionCheck(session) {
+ for {
+ IgniteActionParameters(igniteApi, _) <- resolveIgniteParameters(session)
+ } yield {
+ logger.debug(s"session user id: #${session.userId}, before $request")
+
+ val func = getOrCreateCache(igniteApi, cacheName, config)
+
+ call(func, session)
+ }
+ }
+
+ private def getOrCreateCache(
+ igniteApi: IgniteApi,
+ cacheName: String,
+ config: Configuration
+ ): (CacheApi[K, V] => Unit, Throwable => Unit) => Unit = config match {
+ case cfg: SimpleCacheConfiguration => igniteApi.getOrCreateCacheBySimpleConfig(cacheName, cfg)
+
+ case ThinConfiguration(cfg) =>
+ if (cfg.getName == null) {
+ cfg.setName(cacheName)
+ }
+
+ igniteApi.getOrCreateCacheByClientConfiguration(cfg)
+
+ case ThickConfiguration(cfg) =>
+ if (cfg.getName == null) {
+ cfg.setName(cacheName)
+ }
+
+ igniteApi.getOrCreateCacheByConfiguration(cfg.asInstanceOf[CacheConfiguration[K, V]])
+ }
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/ignite/LambdaAction.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/ignite/LambdaAction.scala
new file mode 100644
index 00000000..33747e99
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/ignite/LambdaAction.scala
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.action.ignite
+
+import scala.util.Try
+
+import io.gatling.core.action.Action
+import io.gatling.core.session.Session
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.gatling.action.IgniteAction
+import org.apache.ignite.gatling.api.IgniteApi
+
+/**
+ * Action executing the arbitrary lambda function accepting Ignite API instance and session.
+ *
+ * Lambda function may make changes in the session instance passed (say save some result into the session).
+ *
+ * To report the operation failure lambda function is assumed to throw the exception.
+ *
+ * @tparam I Ignite Api class.
+ * @param requestName Name of the request.
+ * @param function Lambda function to be executed.
+ * @param next Next action from chain to invoke upon this one completion.
+ * @param ctx Scenario context.
+ */
+class LambdaAction[I](requestName: String, function: (I, Session) => Any, next: Action, ctx: ScenarioContext)
+ extends IgniteAction("lambda", requestName, ctx, next) {
+
+ override protected def execute(session: Session): Unit = withSessionCheck(session) {
+ for {
+ IgniteActionParameters(igniteApi, _) <- resolveIgniteParameters(session)
+ } yield {
+ val func = withCompletion(function, session, igniteApi) _
+
+ call(func, session)
+ }
+ }
+
+ private def withCompletion(function: (I, Session) => Any, session: Session, igniteApi: IgniteApi)(
+ s: Any => Unit,
+ f: Throwable => Unit
+ ): Unit = Try {
+ function(igniteApi.wrapped, session)
+ }.fold(f, s)
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/ignite/StartClientAction.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/ignite/StartClientAction.scala
new file mode 100644
index 00000000..3ec15be4
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/ignite/StartClientAction.scala
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.action.ignite
+
+import io.gatling.commons.validation.Validation
+import io.gatling.core.action.Action
+import io.gatling.core.session.Session
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.gatling.action.IgniteAction
+import org.apache.ignite.gatling.api.IgniteApi
+import org.apache.ignite.gatling.protocol.IgniteProtocol.IgniteApiSessionKey
+
+/**
+ * Action for the start Ignite API operation (starts either the thin client or the node working in client mode).
+ *
+ * @param requestName Name of the request.
+ * @param next Next action from chain to invoke upon this one completion.
+ * @param ctx Scenario context.
+ */
+class StartClientAction(requestName: String, next: Action, ctx: ScenarioContext)
+ extends IgniteAction("start", requestName, ctx, next) {
+
+ override protected def execute(session: Session): Unit = withSessionCheck(session) {
+ logger.debug(s"session user id: #${session.userId}, before $request")
+
+ val func = IgniteApi.start(protocol, session) _
+
+ call(
+ func,
+ session,
+ updateSession = (session, igniteApi: Option[IgniteApi]) =>
+ igniteApi
+ .map(igniteApi => session.set(IgniteApiSessionKey, igniteApi))
+ .getOrElse(session)
+ )
+ Validation.unit
+ }
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/ignite/TransactionCloseAction.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/ignite/TransactionCloseAction.scala
new file mode 100644
index 00000000..5633e6c1
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/ignite/TransactionCloseAction.scala
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.action.ignite
+
+import io.gatling.core.action.Action
+import io.gatling.core.session.Session
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.gatling.action.IgniteAction
+import org.apache.ignite.gatling.protocol.IgniteProtocol.TransactionApiSessionKey
+
+/**
+ * Action for the transaction close Ignite operation.
+ *
+ * @param requestName Name of the request.
+ * @param next Next action from chain to invoke upon this one completion.
+ * @param ctx Scenario context.
+ */
+class TransactionCloseAction(requestName: String, next: Action, ctx: ScenarioContext)
+ extends IgniteAction("txClose", requestName, ctx, next) {
+
+ override val request: String = actionType
+
+ override protected def execute(session: Session): Unit =
+ for {
+ IgniteActionParameters(_, transactionApiOptional) <- resolveIgniteParameters(session)
+ } yield {
+ logger.debug(s"session user id: #${session.userId}, before $request $actionType")
+
+ transactionApiOptional.fold {
+ next ! session
+ } { transactionApi =>
+ call(
+ transactionApi.close,
+ session,
+ updateSession = (session, _: Option[Unit]) => session.remove(TransactionApiSessionKey)
+ )
+ }
+ }
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/ignite/TransactionCommitAction.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/ignite/TransactionCommitAction.scala
new file mode 100644
index 00000000..31f55a20
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/ignite/TransactionCommitAction.scala
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.action.ignite
+
+import io.gatling.commons.validation.Failure
+import io.gatling.commons.validation.Success
+import io.gatling.commons.validation.Validation
+import io.gatling.core.action.Action
+import io.gatling.core.session.Session
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.gatling.action.IgniteAction
+import org.apache.ignite.gatling.api.TransactionApi
+
+/**
+ * Action for the transaction commit Ignite operation.
+ *
+ * @param requestName Name of the request.
+ * @param next Next action from chain to invoke upon this one completion.
+ * @param ctx Scenario context.
+ */
+class TransactionCommitAction(requestName: String, next: Action, ctx: ScenarioContext)
+ extends IgniteAction("commit", requestName, ctx, next) {
+
+ override val request: String = if (requestName == "") actionType else requestName
+
+ override protected def execute(session: Session): Unit = withSessionCheck(session) {
+ for {
+ IgniteActionParameters(_, transactionApiOptional) <- resolveIgniteParameters(session)
+
+ transactionApi <- transactionApiOptional.fold[Validation[TransactionApi]](Failure("no transaction found in session"))(t =>
+ Success(t)
+ )
+ } yield {
+ logger.debug(s"session user id: #${session.userId}, before $request")
+
+ call(
+ transactionApi.commit,
+ session
+ )
+ }
+ }
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/ignite/TransactionRollbackAction.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/ignite/TransactionRollbackAction.scala
new file mode 100644
index 00000000..1f42c7c1
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/ignite/TransactionRollbackAction.scala
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.action.ignite
+
+import io.gatling.commons.validation.Failure
+import io.gatling.commons.validation.Success
+import io.gatling.commons.validation.Validation
+import io.gatling.core.action.Action
+import io.gatling.core.session.Session
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.gatling.action.IgniteAction
+import org.apache.ignite.gatling.api.TransactionApi
+
+/**
+ * Action for the transaction rollback Ignite operation.
+ *
+ * @param requestName Name of the request.
+ * @param next Next action from chain to invoke upon this one completion.
+ * @param ctx Scenario context.
+ */
+class TransactionRollbackAction(requestName: String, next: Action, ctx: ScenarioContext)
+ extends IgniteAction("rollback", requestName, ctx, next) {
+
+ override val request: String = if (requestName == "") actionType else requestName
+
+ override protected def execute(session: Session): Unit = withSessionCheck(session) {
+ for {
+ IgniteActionParameters(_, transactionApiOptional) <- resolveIgniteParameters(session)
+
+ transactionApi <- transactionApiOptional.fold[Validation[TransactionApi]](Failure("no transaction found in session"))(t =>
+ Success(t)
+ )
+ } yield {
+ logger.debug(s"session user id: #${session.userId}, before $request")
+
+ call(
+ transactionApi.rollback,
+ session
+ )
+ }
+ }
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/ignite/TransactionStartAction.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/ignite/TransactionStartAction.scala
new file mode 100644
index 00000000..66e0d44d
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/action/ignite/TransactionStartAction.scala
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.action.ignite
+
+import io.gatling.core.action.Action
+import io.gatling.core.session.Session
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.gatling.action.IgniteAction
+import org.apache.ignite.gatling.api.IgniteApi
+import org.apache.ignite.gatling.api.TransactionApi
+import org.apache.ignite.gatling.builder.transaction.TransactionParameters
+import org.apache.ignite.gatling.protocol.IgniteProtocol.TransactionApiSessionKey
+import org.apache.ignite.transactions.TransactionConcurrency
+import org.apache.ignite.transactions.TransactionIsolation
+
+/**
+ * Action for the transaction rollback Ignite operation.
+ *
+ * @param requestName Name of the request.
+ * @param params Transaction parameters.
+ * @param next Next action from chain to invoke upon this one completion.
+ * @param ctx Scenario context.
+ */
+class TransactionStartAction(requestName: String, params: TransactionParameters, next: Action, ctx: ScenarioContext)
+ extends IgniteAction("txStart", requestName, ctx, next) {
+
+ override val request: String = actionType
+
+ override protected def execute(session: Session): Unit = withSessionCheck(session) {
+ for {
+ IgniteActionParameters(igniteApi, _) <- resolveIgniteParameters(session)
+ } yield {
+ logger.debug(s"session user id: #${session.userId}, before $request $actionType")
+
+ val func = txStart(igniteApi, params.concurrency, params.isolation, params.timeout, params.size)
+
+ call(
+ func,
+ session,
+ updateSession = (session, transactionApi: Option[TransactionApi]) =>
+ transactionApi
+ .map(transactionApi => session.set(TransactionApiSessionKey, transactionApi))
+ .getOrElse(session)
+ )
+ }
+ }
+
+ private def txStart(
+ igniteApi: IgniteApi,
+ concurrency: Option[TransactionConcurrency],
+ isolation: Option[TransactionIsolation],
+ timeout: Option[Long],
+ size: Option[Int]
+ ): (TransactionApi => Unit, Throwable => Unit) => Unit =
+ if (isolation.isDefined && concurrency.isDefined) {
+ if (timeout.isDefined) {
+ igniteApi.txStart(params.concurrency.get, params.isolation.get, timeout.get, size.getOrElse(0))
+ } else {
+ igniteApi.txStart(params.concurrency.get, params.isolation.get)
+ }
+ } else {
+ igniteApi.txStart()
+ }
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/api/CacheApi.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/api/CacheApi.scala
new file mode 100644
index 00000000..bedd7bd7
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/api/CacheApi.scala
@@ -0,0 +1,280 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.api
+
+import java.util.concurrent.locks.Lock
+
+import javax.cache.processor.EntryProcessorResult
+
+import scala.collection.SortedMap
+import scala.collection.SortedSet
+
+import org.apache.ignite.cache.CacheEntryProcessor
+import org.apache.ignite.cache.query.SqlFieldsQuery
+
+/**
+ * Wrapper around the Ignite Cache API abstracting access either via the Ignite node (thick) API or vai the
+ * Ignite Client (thin) API.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ */
+trait CacheApi[K, V] {
+ /**
+ * Puts entry to cache.
+ *
+ * @param key Key.
+ * @param value Value.
+ * @param s Function to be called if operation is competed successfully.
+ * @param f Function to be called if exception occurs.
+ */
+ def put(key: K, value: V)(s: Unit => Unit, f: Throwable => Unit): Unit
+
+ /**
+ * Asynchronously puts entry to cache.
+ *
+ * @param key Key.
+ * @param value Value.
+ * @param s Function to be called if operation is competed successfully.
+ * @param f Function to be called if exception occurs.
+ */
+ def putAsync(key: K, value: V)(s: Unit => Unit, f: Throwable => Unit): Unit
+
+ /**
+ * Puts batch of entries to cache.
+ *
+ * @param map Key / value pairs map.
+ * @param s Function to be called if operation is competed successfully.
+ * @param f Function to be called if exception occurs.
+ */
+ def putAll(map: SortedMap[K, V])(s: Unit => Unit, f: Throwable => Unit): Unit
+
+ /**
+ * Asynchronously puts batch of entries to cache.
+ *
+ * @param map Key / value pairs map.
+ * @param s Function to be called if operation is competed successfully.
+ * @param f Function to be called if exception occurs.
+ */
+ def putAllAsync(map: SortedMap[K, V])(s: Unit => Unit, f: Throwable => Unit): Unit
+
+ /**
+ * Gets an entry from cache by key.
+ *
+ * @param key Key.
+ * @param s Function to be called if operation is competed successfully.
+ * @param f Function to be called if exception occurs.
+ */
+ def get(key: K)(s: Map[K, V] => Unit, f: Throwable => Unit): Unit
+
+ /**
+ * Asynchronously gets an entry from cache by key.
+ *
+ * @param key Key.
+ * @param s Function to be called if operation is competed successfully.
+ * @param f Function to be called if exception occurs.
+ */
+ def getAsync(key: K)(s: Map[K, V] => Unit, f: Throwable => Unit): Unit
+
+ /**
+ * Gets a collection of entries from the cache.
+ *
+ * @param keys Collection of keys.
+ * @param s Function to be called if operation is competed successfully.
+ * @param f Function to be called if exception occurs.
+ */
+ def getAll(keys: SortedSet[K])(s: Map[K, V] => Unit, f: Throwable => Unit): Unit
+
+ /**
+ * Asynchronously gets a collection of entries from the cache.
+ *
+ * @param keys Collection of keys.
+ * @param s Function to be called if operation is competed successfully.
+ * @param f Function to be called if exception occurs.
+ */
+ def getAllAsync(keys: SortedSet[K])(s: Map[K, V] => Unit, f: Throwable => Unit): Unit
+
+ /**
+ * Removes the entry for a key only if currently mapped to some.
+ *
+ * @param key Key.
+ * @param s Function to be called if operation is competed successfully.
+ * @param f Function to be called if exception occurs.
+ */
+ def getAndRemove(key: K)(s: Map[K, V] => Unit, f: Throwable => Unit): Unit
+
+ /**
+ * Asynchronously removes the entry for a key only if currently mapped to some.
+ *
+ * @param key Key.
+ * @param s Function to be called if operation is competed successfully.
+ * @param f Function to be called if exception occurs.
+ */
+ def getAndRemoveAsync(key: K)(s: Map[K, V] => Unit, f: Throwable => Unit): Unit
+
+ /**
+ * Puts value with the specified key in this cache, returning an existing value
+ * if one existed.
+ *
+ * @param key Key.
+ * @param value Value.
+ * @param s Function to be called if operation is competed successfully.
+ * @param f Function to be called if exception occurs.
+ */
+ def getAndPut(key: K, value: V)(s: Map[K, V] => Unit, f: Throwable => Unit): Unit
+
+ /**
+ * Asynchronously puts value with the specified key in this cache, returning an existing
+ * value if one existed.
+ *
+ * @param key Key.
+ * @param value Value.
+ * @param s Function to be called if operation is competed successfully.
+ * @param f Function to be called if exception occurs.
+ */
+ def getAndPutAsync(key: K, value: V)(s: Map[K, V] => Unit, f: Throwable => Unit): Unit
+
+ /**
+ * Removes the entry for a key.
+ *
+ * @param key Key.
+ * @param s Function to be called if operation is competed successfully.
+ * @param f Function to be called if exception occurs.
+ */
+ def remove(key: K)(s: Unit => Unit, f: Throwable => Unit): Unit
+
+ /**
+ * Asynchronously removes the entry for a key.
+ *
+ * @param key Key.
+ * @param s Function to be called if operation is competed successfully.
+ * @param f Function to be called if exception occurs.
+ */
+ def removeAsync(key: K)(s: Unit => Unit, f: Throwable => Unit): Unit
+
+ /**
+ * Removes a collection of entries from the cache.
+ *
+ * @param keys Set of keys.
+ * @param s Function to be called if operation is competed successfully.
+ * @param f Function to be called if exception occurs.
+ */
+ def removeAll(keys: SortedSet[K])(s: Unit => Unit, f: Throwable => Unit): Unit
+
+ /**
+ * Asynchronously removes a collection of entries from the cache.
+ *
+ * @param keys Set of keys.
+ * @param s Function to be called if operation is competed successfully.
+ * @param f Function to be called if exception occurs.
+ */
+ def removeAllAsync(keys: SortedSet[K])(s: Unit => Unit, f: Throwable => Unit): Unit
+
+ /**
+ * Invokes an CacheEntryProcessor against the entry specified by the provided key.
+ *
+ * @tparam T Type of the return value.
+ * @param key Key.
+ * @param entryProcessor Entry processor.
+ * @param arguments Additional arguments to pass to the entry processor.
+ * @param s Function to be called if operation is competed successfully.
+ * @param f Function to be called if exception occurs.
+ */
+ def invoke[T](key: K, entryProcessor: CacheEntryProcessor[K, V, T], arguments: Any*)(
+ s: Map[K, T] => Unit,
+ f: Throwable => Unit
+ ): Unit
+
+ /**
+ * Asynchronously invokes an CacheEntryProcessor against the entry specified by the provided key.
+ *
+ * @tparam T Type of the return value.
+ * @param key Key.
+ * @param entryProcessor Entry processor.
+ * @param arguments Additional arguments to pass to the entry processor.
+ * @param s Function to be called if operation is competed successfully.
+ * @param f Function to be called if exception occurs.
+ */
+ def invokeAsync[T](key: K, entryProcessor: CacheEntryProcessor[K, V, T], arguments: Any*)(
+ s: Map[K, T] => Unit,
+ f: Throwable => Unit
+ ): Unit
+
+ /**
+ * Invokes each EntryProcessor from map's values against the correspondent
+ * entry specified by map's key set.
+ *
+ * @tparam T Type of the return value.
+ * @param map Map containing keys and entry processors to be applied to values.
+ * @param arguments Additional arguments to pass to the entry processors.
+ * @param s Function to be called if operation is competed successfully.
+ * @param f Function to be called if exception occurs.
+ */
+ def invokeAll[T](map: SortedMap[K, CacheEntryProcessor[K, V, T]], arguments: Any*)(
+ s: Map[K, EntryProcessorResult[T]] => Unit,
+ f: Throwable => Unit
+ ): Unit
+
+ /**
+ * Asynchronously invokes each EntryProcessor from map's values against the correspondent
+ * entry specified by map's key set.
+ *
+ * @tparam T Type of the return value.
+ * @param map Map containing keys and entry processors to be applied to values.
+ * @param arguments Additional arguments to pass to the entry processors.
+ * @param s Function to be called if operation is competed successfully.
+ * @param f Function to be called if exception occurs.
+ */
+ def invokeAllAsync[T](map: SortedMap[K, CacheEntryProcessor[K, V, T]], arguments: Any*)(
+ s: Map[K, EntryProcessorResult[T]] => Unit,
+ f: Throwable => Unit
+ ): Unit
+
+ /**
+ * Acquires lock associated with a passed key.
+ *
+ * @param key Key.
+ * @param s Function to be called if operation is competed successfully.
+ * @param f Function to be called if exception occurs.
+ */
+ def lock(key: K)(s: Lock => Unit, f: Throwable => Unit): Unit
+
+ /**
+ * Releases the lock.
+ *
+ * @param lock Lock.
+ * @param s Function to be called if operation is competed successfully.
+ * @param f Function to be called if exception occurs.
+ */
+ def unlock(lock: Lock)(s: Unit => Unit, f: Throwable => Unit): Unit
+
+ /**
+ * Executes the Ignite SqlFieldsQuery
+ *
+ * @param query Query object.
+ * @param s Function to be called if operation is competed successfully.
+ * @param f Function to be called if exception occurs.
+ */
+ def sql(query: SqlFieldsQuery)(s: List[List[Any]] => Unit, f: Throwable => Unit): Unit
+
+ /**
+ * Returns cache that will operate with binary objects.
+ *
+ * @return Instance of Cache API.
+ */
+ def withKeepBinary(): CacheApi[K, V]
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/api/IgniteApi.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/api/IgniteApi.scala
new file mode 100644
index 00000000..e4ceefab
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/api/IgniteApi.scala
@@ -0,0 +1,243 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.api
+
+import scala.util.Try
+
+import com.typesafe.scalalogging.StrictLogging
+import io.gatling.commons.validation.Validation
+import io.gatling.core.session.Session
+import org.apache.ignite.Ignition
+import org.apache.ignite.binary.BinaryObjectBuilder
+import org.apache.ignite.client.ClientCacheConfiguration
+import org.apache.ignite.client.IgniteClient
+import org.apache.ignite.configuration.CacheConfiguration
+import org.apache.ignite.configuration.ClientConfiguration
+import org.apache.ignite.configuration.IgniteConfiguration
+import org.apache.ignite.gatling.api.node.IgniteNodeApi
+import org.apache.ignite.gatling.api.thin.IgniteThinApi
+import org.apache.ignite.gatling.builder.ignite.SimpleCacheConfiguration
+import org.apache.ignite.gatling.protocol.IgniteClientCfg
+import org.apache.ignite.gatling.protocol.IgniteClientConfigurationCfg
+import org.apache.ignite.gatling.protocol.IgniteClientPool
+import org.apache.ignite.gatling.protocol.IgniteClientPoolCfg
+import org.apache.ignite.gatling.protocol.IgniteConfigurationCfg
+import org.apache.ignite.gatling.protocol.IgniteNodeCfg
+import org.apache.ignite.gatling.protocol.IgniteProtocol
+import org.apache.ignite.gatling.protocol.IgniteProtocol.IgniteApiSessionKey
+import org.apache.ignite.transactions.TransactionConcurrency
+import org.apache.ignite.transactions.TransactionIsolation
+
+/**
+ * Wrapper around the Ignite API abstracting access either via the Ignite Node (thick) API or via the
+ * Ignite Client (thin) API.
+ */
+trait IgniteApi {
+ /**
+ * Gets an instance of cache API by name.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @return Instance of the CacheAPI.
+ */
+ def cache[K, V]: String => Validation[CacheApi[K, V]]
+
+ /**
+ * Gets existing cache or creates new one with the given name and the simplified cache configuration
+ * which may be defined via the Ignite Gatling DSL.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @param name Cache name.
+ * @param cfg Simplified configuration of the cache (see the SimpleCacheConfiguration).
+ * @param s Function to be called if operation is competed successfully.
+ * @param f Function to be called if exception occurs.
+ */
+ def getOrCreateCacheBySimpleConfig[K, V](name: String, cfg: SimpleCacheConfiguration)(
+ s: CacheApi[K, V] => Unit,
+ f: Throwable => Unit
+ ): Unit
+
+ /**
+ * Gets existing cache or creates new one with the provided Ignite (thin) Client cache configuration.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @param cfg Instance of Ignite Client cache configuration.
+ * @param s Function to be called if operation is competed successfully.
+ * @param f Function to be called if exception occurs.
+ */
+ def getOrCreateCacheByClientConfiguration[K, V](
+ cfg: ClientCacheConfiguration
+ )(s: CacheApi[K, V] => Unit, f: Throwable => Unit): Unit
+
+ /**
+ * Gets existing cache or creates new one with the provided Ignite (thick) cache configuration.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @param cfg Instance of Ignite cache configuration.
+ * @param s Function to be called if operation is competed successfully.
+ * @param f Function to be called if exception occurs.
+ */
+ def getOrCreateCacheByConfiguration[K, V](cfg: CacheConfiguration[K, V])(s: CacheApi[K, V] => Unit, f: Throwable => Unit): Unit
+
+ /**
+ * Closes the Ignite API.
+ *
+ * @param s Function to be called if operation is competed successfully.
+ * @param f Function to be called if exception occurs.
+ */
+ def close()(s: Unit => Unit, f: Throwable => Unit): Unit
+
+ /**
+ * Starts the transaction with the default parameters.
+ *
+ * @param s Function to be called if operation is competed successfully.
+ * @param f Function to be called if exception occurs.
+ */
+ def txStart()(s: TransactionApi => Unit, f: Throwable => Unit): Unit
+
+ /**
+ * Starts the transaction with the provided concurrency and isolation.
+ *
+ * @param concurrency Concurrency.
+ * @param isolation Isolation.
+ * @param s Function to be called if operation is competed successfully.
+ * @param f Function to be called if exception occurs.
+ */
+ def txStart(concurrency: TransactionConcurrency, isolation: TransactionIsolation)(
+ s: TransactionApi => Unit,
+ f: Throwable => Unit
+ ): Unit
+
+ /**
+ * Starts the transaction with the provided concurrency, isolation timeout and transaction size.
+ *
+ * @param concurrency Concurrency.
+ * @param isolation Isolation.
+ * @param timeout Timeout.
+ * @param size Number of entries participating in transaction (may be approximate).
+ * @param s Function to be called if operation is competed successfully.
+ * @param f Function to be called if exception occurs.
+ */
+ def txStart(concurrency: TransactionConcurrency, isolation: TransactionIsolation, timeout: Long, size: Int)(
+ s: TransactionApi => Unit,
+ f: Throwable => Unit
+ ): Unit
+
+ /**
+ * Creates instance of BinaryObjectBuilder.
+ *
+ * @return Instance of BinaryObjectBuilder.
+ */
+ def binaryObjectBuilder: String => BinaryObjectBuilder
+
+ /**
+ * Returns the wrapped Ignite API instance.
+ *
+ * @tparam I Class of the Ignite API.
+ * @return wrapped Ignite API instance.
+ */
+ def wrapped[I]: I
+}
+
+/**
+ * Factory for IgniteApi instances.
+ */
+object IgniteApi extends StrictLogging {
+ /**
+ * Starts new instance of IgniteApi.
+ *
+ * Starts new Ignite node or thin client instances if protocol was configured via configuration objects.
+ * Do nothing and returns existing instances from session otherwise.
+ *
+ * @param protocol Gatling Ignite protocol.
+ * @param session Gatling session.
+ * @param s Function to be called if operation is competed successfully.
+ * @param f Function to be called if exception occurs.
+ */
+ def start(protocol: IgniteProtocol, session: Session)(s: IgniteApi => Unit, f: Throwable => Unit): Unit =
+ protocol.cfg match {
+ case IgniteClientConfigurationCfg(cfg) if protocol.explicitClientStart => Try(startClient(cfg)).fold(f, s)
+
+ case IgniteConfigurationCfg(cfg) if protocol.explicitClientStart => Try(startNode(cfg)).fold(f, s)
+
+ case _ => s(session(IgniteApiSessionKey).as[IgniteApi])
+ }
+
+ /**
+ * Creates default instance of IgniteApi on simulation start depending on the protocol passed.
+ *
+ * If protocol contains configuration objects it starts either ignite node
+ * in client mode or thin ignite client.
+ *
+ * If protocol contains started instances of node or client just returns it.
+ *
+ * @param protocol Gatling Ignite protocol
+ * @return Instance of Ignite API
+ */
+ def apply(protocol: IgniteProtocol): Option[IgniteApi] =
+ protocol.cfg match {
+ case IgniteClientConfigurationCfg(cfg) if !protocol.explicitClientStart => Some(startClient(cfg))
+
+ case IgniteConfigurationCfg(cfg) if !protocol.explicitClientStart => Some(startNode(cfg))
+
+ case IgniteNodeCfg(node) =>
+ node.cacheNames.forEach(name => node.cache(name))
+ Some(IgniteNodeApi(node))
+
+ case IgniteClientCfg(igniteClient) => Some(IgniteThinApi(new IgniteClientPreStartedPool(igniteClient)))
+
+ case IgniteClientPoolCfg(pool) => Some(IgniteThinApi(pool))
+
+ case _ => None
+ }
+
+ /**
+ * Starts Ignite node in client mode.
+ *
+ * @param cfg Node configuration.
+ * @return Instance of IgniteNodeApi.
+ */
+ private def startNode(cfg: IgniteConfiguration): IgniteNodeApi = {
+ val node = Ignition.start(cfg)
+ node.cacheNames.forEach(name => node.cache(name))
+ IgniteNodeApi(node)
+ }
+
+ /**
+ * Starts Ignite (thin) client.
+ *
+ * @param cfg Client configuration.
+ * @return Instance of IgniteThinApi.
+ */
+ private def startClient(cfg: ClientConfiguration): IgniteThinApi = IgniteThinApi(
+ new IgniteClientPreStartedPool(Ignition.startClient(cfg))
+ )
+}
+
+/**
+ * Pool enclosing the single pre-started IgniteClient instance.
+ *
+ * @param client Thin client instance.
+ */
+private class IgniteClientPreStartedPool(client: IgniteClient) extends IgniteClientPool {
+ override def apply(): IgniteClient = client
+
+ override def close(): Unit = {}
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/api/TransactionApi.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/api/TransactionApi.scala
new file mode 100644
index 00000000..49ae0638
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/api/TransactionApi.scala
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.api
+
+/**
+ * Wrapper around the Ignite org.apache.ignite.transactions.Transaction object.
+ */
+trait TransactionApi {
+ /**
+ * Commits the enclosed transaction
+ *
+ * @param s Function to be called if operation is competed successfully.
+ * @param f Function to be called if exception occurs.
+ */
+ def commit(s: Unit => Unit, f: Throwable => Unit): Unit
+
+ /**
+ * Rollbacks the enclosed transaction
+ *
+ * @param s Function to be called if operation is competed successfully.
+ * @param f Function to be called if exception occurs.
+ */
+ def rollback(s: Unit => Unit, f: Throwable => Unit): Unit
+
+ /**
+ * Closes the enclosed transaction
+ *
+ * @param s Function to be called if operation is competed successfully.
+ * @param f Function to be called if exception occurs.
+ */
+ def close(s: Unit => Unit, f: Throwable => Unit): Unit
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/api/node/CacheNodeApi.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/api/node/CacheNodeApi.scala
new file mode 100644
index 00000000..7572d13e
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/api/node/CacheNodeApi.scala
@@ -0,0 +1,216 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.api.node
+
+import java.util.concurrent.locks.Lock
+
+import javax.cache.processor.EntryProcessorResult
+
+import scala.collection.SortedMap
+import scala.collection.SortedSet
+import scala.jdk.CollectionConverters._
+import scala.util.Try
+
+import com.typesafe.scalalogging.StrictLogging
+import org.apache.ignite.IgniteCache
+import org.apache.ignite.cache.CacheEntryProcessor
+import org.apache.ignite.cache.query.SqlFieldsQuery
+import org.apache.ignite.gatling.api.CacheApi
+
+/**
+ * Implementation of CacheApi working via the Ignite Node (thick) API.
+ *
+ * @param wrapped Instance of Cache API.
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ */
+case class CacheNodeApi[K, V](wrapped: IgniteCache[K, V]) extends CacheApi[K, V] with StrictLogging {
+ override def put(key: K, value: V)(s: Unit => Unit, f: Throwable => Unit): Unit =
+ Try(wrapped.put(key, value))
+ .map(_ => ())
+ .fold(f, s)
+
+ override def putAsync(key: K, value: V)(s: Unit => Unit, f: Throwable => Unit): Unit =
+ wrapped
+ .putAsync(key, value)
+ .listen(future =>
+ Try(future.get())
+ .map(_ => ())
+ .fold(f, s)
+ )
+
+ override def putAll(map: SortedMap[K, V])(s: Unit => Unit, f: Throwable => Unit): Unit =
+ Try(wrapped.putAll(map.asJava))
+ .map(_ => ())
+ .fold(f, s)
+
+ override def putAllAsync(map: SortedMap[K, V])(s: Unit => Unit, f: Throwable => Unit): Unit =
+ wrapped
+ .putAllAsync(map.asJava)
+ .listen(future =>
+ Try(future.get())
+ .map(_ => ())
+ .fold(f, s)
+ )
+
+ override def get(key: K)(s: Map[K, V] => Unit, f: Throwable => Unit): Unit =
+ Try(wrapped.get(key))
+ .map(value => Map((key, value)))
+ .fold(f, s)
+
+ override def getAsync(key: K)(s: Map[K, V] => Unit, f: Throwable => Unit): Unit =
+ wrapped
+ .getAsync(key)
+ .listen(future =>
+ Try(future.get())
+ .map(value => Map((key, value)))
+ .fold(f, s)
+ )
+
+ override def getAndRemove(key: K)(s: Map[K, V] => Unit, f: Throwable => Unit): Unit =
+ Try(wrapped.getAndRemove(key))
+ .map(value => Map((key, value)))
+ .fold(f, s)
+
+ override def getAndRemoveAsync(key: K)(s: Map[K, V] => Unit, f: Throwable => Unit): Unit =
+ wrapped
+ .getAndRemoveAsync(key)
+ .listen(future =>
+ Try(future.get())
+ .map(value => Map((key, value)))
+ .fold(f, s)
+ )
+
+ override def getAndPut(key: K, value: V)(s: Map[K, V] => Unit, f: Throwable => Unit): Unit =
+ Try(wrapped.getAndPut(key, value))
+ .map(value => Map((key, value)))
+ .fold(f, s)
+
+ override def getAndPutAsync(key: K, value: V)(s: Map[K, V] => Unit, f: Throwable => Unit): Unit =
+ wrapped
+ .getAndPutAsync(key, value)
+ .listen(future =>
+ Try(future.get())
+ .map(value => Map((key, value)))
+ .fold(f, s)
+ )
+
+ override def getAll(keys: SortedSet[K])(s: Map[K, V] => Unit, f: Throwable => Unit): Unit =
+ Try(wrapped.getAll(keys.asJava))
+ .map(_.asScala.toMap)
+ .fold(f, s)
+
+ override def getAllAsync(keys: SortedSet[K])(s: Map[K, V] => Unit, f: Throwable => Unit): Unit =
+ wrapped
+ .getAllAsync(keys.asJava)
+ .listen(future =>
+ Try(future.get())
+ .map(_.asScala.toMap)
+ .fold(f, s)
+ )
+
+ override def remove(key: K)(s: Unit => Unit, f: Throwable => Unit): Unit =
+ Try(wrapped.remove(key))
+ .map(_ => ())
+ .fold(f, s)
+
+ override def removeAsync(key: K)(s: Unit => Unit, f: Throwable => Unit): Unit =
+ wrapped
+ .removeAsync(key)
+ .listen(future =>
+ Try(future.get())
+ .map(_ => ())
+ .fold(f, s)
+ )
+
+ override def removeAll(keys: SortedSet[K])(s: Unit => Unit, f: Throwable => Unit): Unit =
+ Try(wrapped.removeAll(keys.asJava))
+ .map(_ => ())
+ .fold(f, s)
+
+ override def removeAllAsync(keys: SortedSet[K])(s: Unit => Unit, f: Throwable => Unit): Unit =
+ wrapped
+ .removeAllAsync(keys.asJava)
+ .listen(future =>
+ Try(future.get())
+ .map(_ => ())
+ .fold(f, s)
+ )
+
+ override def invoke[T](key: K, entryProcessor: CacheEntryProcessor[K, V, T], arguments: Any*)(
+ s: Map[K, T] => Unit,
+ f: Throwable => Unit
+ ): Unit =
+ Try(wrapped.invoke[T](key, entryProcessor, arguments: _*))
+ .map(value => Map((key, value)))
+ .fold(f, s)
+
+ override def invokeAsync[T](key: K, entryProcessor: CacheEntryProcessor[K, V, T], arguments: Any*)(
+ s: Map[K, T] => Unit,
+ f: Throwable => Unit
+ ): Unit =
+ wrapped
+ .invokeAsync(key, entryProcessor, arguments: _*)
+ .listen(future =>
+ Try(future.get())
+ .map(value => Map((key, value)))
+ .fold(f, s)
+ )
+
+ override def invokeAll[T](
+ map: SortedMap[K, CacheEntryProcessor[K, V, T]],
+ arguments: Any*
+ )(s: Map[K, EntryProcessorResult[T]] => Unit, f: Throwable => Unit): Unit =
+ Try(wrapped.invokeAll[T](map.asJava, arguments: _*))
+ .map(_.asScala.toMap)
+ .fold(f, s)
+
+ override def invokeAllAsync[T](
+ map: SortedMap[K, CacheEntryProcessor[K, V, T]],
+ arguments: Any*
+ )(s: Map[K, EntryProcessorResult[T]] => Unit, f: Throwable => Unit): Unit =
+ wrapped
+ .invokeAllAsync(map.asJava, arguments: _*)
+ .listen(future =>
+ Try(future.get())
+ .map(_.asScala.toMap)
+ .fold(f, s)
+ )
+
+ override def lock(key: K)(s: Lock => Unit, f: Throwable => Unit): Unit =
+ Try {
+ val lock = wrapped.lock(key)
+ lock.lock()
+ lock
+ }.fold(f, s)
+
+ override def unlock(lock: Lock)(s: Unit => Unit, f: Throwable => Unit): Unit =
+ Try(lock.unlock())
+ .fold(f, s)
+
+ override def sql(query: SqlFieldsQuery)(s: List[List[Any]] => Unit, f: Throwable => Unit): Unit =
+ Try(
+ wrapped
+ .query(query)
+ .getAll
+ .asScala
+ .toList
+ .map(_.asScala.toList)
+ ).fold(f, s)
+
+ override def withKeepBinary(): CacheApi[K, V] = copy(wrapped = wrapped.withKeepBinary())
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/api/node/IgniteNodeApi.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/api/node/IgniteNodeApi.scala
new file mode 100644
index 00000000..4837306c
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/api/node/IgniteNodeApi.scala
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.api.node
+
+import scala.util.Try
+
+import io.gatling.commons.validation.{Failure => ValidationFailure}
+import io.gatling.commons.validation.{Success => ValidationSuccess}
+import io.gatling.commons.validation.Validation
+import org.apache.ignite.Ignite
+import org.apache.ignite.binary.BinaryObjectBuilder
+import org.apache.ignite.client.ClientCacheConfiguration
+import org.apache.ignite.configuration.CacheConfiguration
+import org.apache.ignite.gatling.api.CacheApi
+import org.apache.ignite.gatling.api.IgniteApi
+import org.apache.ignite.gatling.api.TransactionApi
+import org.apache.ignite.gatling.builder.ignite.SimpleCacheConfiguration
+import org.apache.ignite.transactions.TransactionConcurrency
+import org.apache.ignite.transactions.TransactionIsolation
+import scalaz.Memo
+
+/**
+ * Implementation of IgniteApi working via the Ignite node (thick) API.
+ *
+ * @param wrapped Instance of Ignite API.
+ */
+case class IgniteNodeApi(wrapped: Ignite) extends IgniteApi {
+
+ override def cache[K, V]: String => Validation[CacheApi[K, V]] = Memo.immutableHashMapMemo[String, Validation[CacheApi[K, V]]] { name =>
+ Try(wrapped.cache[K, V](name))
+ .map(CacheNodeApi[K, V])
+ .fold(ex => ValidationFailure(ex.getMessage), ValidationSuccess(_))
+ }
+
+ override def getOrCreateCacheByClientConfiguration[K, V](
+ cfg: ClientCacheConfiguration
+ )(s: CacheApi[K, V] => Unit, f: Throwable => Unit): Unit =
+ f(new NotImplementedError("Thin client cache configuration was used to create cache via node API"))
+
+ override def getOrCreateCacheByConfiguration[K, V](
+ cfg: CacheConfiguration[K, V]
+ )(s: CacheApi[K, V] => Unit, f: Throwable => Unit): Unit =
+ Try(wrapped.getOrCreateCache[K, V](cfg))
+ .map(CacheNodeApi[K, V])
+ .fold(f, s)
+
+ override def getOrCreateCacheBySimpleConfig[K, V](name: String, cfg: SimpleCacheConfiguration)(
+ s: CacheApi[K, V] => Unit,
+ f: Throwable => Unit
+ ): Unit =
+ getOrCreateCacheByConfiguration(
+ new CacheConfiguration[K, V]()
+ .setName(name)
+ .setCacheMode(cfg.cacheMode)
+ .setAtomicityMode(cfg.atomicity)
+ .setBackups(cfg.backups)
+ )(s, f)
+
+ override def close()(s: Unit => Unit, f: Throwable => Unit): Unit =
+ Try(wrapped.close()).fold(f, s)
+
+ override def txStart()(s: TransactionApi => Unit, f: Throwable => Unit): Unit =
+ Try(wrapped.transactions().txStart())
+ .map(TransactionNodeApi)
+ .fold(f, s)
+
+ override def txStart(
+ concurrency: TransactionConcurrency,
+ isolation: TransactionIsolation
+ )(s: TransactionApi => Unit, f: Throwable => Unit): Unit =
+ Try(wrapped.transactions().txStart(concurrency, isolation))
+ .map(TransactionNodeApi)
+ .fold(f, s)
+
+ override def txStart(concurrency: TransactionConcurrency, isolation: TransactionIsolation, timeout: Long, size: Int)(
+ s: TransactionApi => Unit,
+ f: Throwable => Unit
+ ): Unit =
+ Try(wrapped.transactions().txStart(concurrency, isolation, timeout, size))
+ .map(TransactionNodeApi)
+ .fold(f, s)
+
+ override def binaryObjectBuilder: String => BinaryObjectBuilder = typeName => wrapped.binary().builder(typeName)
+
+ override def wrapped[I]: I = wrapped.asInstanceOf[I]
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/api/node/TransactionNodeApi.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/api/node/TransactionNodeApi.scala
new file mode 100644
index 00000000..82cd8af4
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/api/node/TransactionNodeApi.scala
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.api.node
+
+import scala.util.Try
+
+import org.apache.ignite.gatling.api.TransactionApi
+import org.apache.ignite.transactions.Transaction
+
+/**
+ * Implementation of TransactionApi working via the Ignite node (thick) API.
+ *
+ * @param wrapped Enclosed Ignite instance.
+ */
+case class TransactionNodeApi(wrapped: Transaction) extends TransactionApi {
+
+ override def commit(s: Unit => Unit, f: Throwable => Unit): Unit =
+ Try(wrapped.commit())
+ .fold(f, s)
+
+ override def rollback(s: Unit => Unit, f: Throwable => Unit): Unit =
+ Try(wrapped.rollback())
+ .fold(f, s)
+
+ override def close(s: Unit => Unit, f: Throwable => Unit): Unit =
+ Try(wrapped.close())
+ .fold(f, s)
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/api/thin/CacheThinApi.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/api/thin/CacheThinApi.scala
new file mode 100644
index 00000000..d07e3249
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/api/thin/CacheThinApi.scala
@@ -0,0 +1,165 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.api.thin
+
+import java.util.concurrent.locks.Lock
+
+import javax.cache.processor.EntryProcessorResult
+
+import scala.collection.SortedMap
+import scala.collection.SortedSet
+import scala.jdk.CollectionConverters._
+import scala.jdk.FutureConverters.CompletionStageOps
+import scala.util.Try
+
+import com.typesafe.scalalogging.StrictLogging
+import org.apache.ignite.cache.CacheEntryProcessor
+import org.apache.ignite.cache.query.SqlFieldsQuery
+import org.apache.ignite.client.ClientCache
+import org.apache.ignite.gatling.api.CacheApi
+
+/**
+ * Implementation of CacheApi working via the Ignite (thin) Client API.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @param wrapped Instance of Ignite Client Cache API.
+ */
+case class CacheThinApi[K, V](wrapped: ClientCache[K, V]) extends CacheApi[K, V] with CompletionSupport with StrictLogging {
+
+ override def put(key: K, value: V)(s: Unit => Unit, f: Throwable => Unit): Unit =
+ Try(wrapped.put(key, value))
+ .map(_ => ())
+ .fold(f, s)
+
+ override def putAsync(key: K, value: V)(s: Unit => Unit, f: Throwable => Unit): Unit =
+ withCompletion {
+ wrapped.putAsync(key, value).asScala.map(_ => ())
+ }(s, f)
+
+ override def putAll(map: SortedMap[K, V])(s: Unit => Unit, f: Throwable => Unit): Unit =
+ Try(wrapped.putAll(map.asJava))
+ .map(_ => ())
+ .fold(f, s)
+
+ override def putAllAsync(map: SortedMap[K, V])(s: Unit => Unit, f: Throwable => Unit): Unit =
+ withCompletion {
+ wrapped.putAllAsync(map.asJava).asScala.map(_ => ())
+ }(s, f)
+
+ override def get(key: K)(s: Map[K, V] => Unit, f: Throwable => Unit): Unit =
+ Try(wrapped.get(key))
+ .map(v => Map((key, v)))
+ .fold(f, s)
+
+ override def getAsync(key: K)(s: Map[K, V] => Unit, f: Throwable => Unit): Unit =
+ withCompletion {
+ wrapped.getAsync(key).asScala.map(v => Map((key, v)))
+ }(s, f)
+
+ override def getAndRemove(key: K)(s: Map[K, V] => Unit, f: Throwable => Unit): Unit =
+ Try(wrapped.getAndRemove(key))
+ .map(v => Map((key, v)))
+ .fold(f, s)
+
+ override def getAndRemoveAsync(key: K)(s: Map[K, V] => Unit, f: Throwable => Unit): Unit =
+ withCompletion {
+ wrapped.getAndRemoveAsync(key).asScala.map(v => Map((key, v)))
+ }(s, f)
+
+ override def getAndPut(key: K, value: V)(s: Map[K, V] => Unit, f: Throwable => Unit): Unit =
+ Try(wrapped.getAndPut(key, value))
+ .map(v => Map((key, v)))
+ .fold(f, s)
+
+ override def getAndPutAsync(key: K, value: V)(s: Map[K, V] => Unit, f: Throwable => Unit): Unit =
+ withCompletion {
+ wrapped.getAndPutAsync(key, value).asScala.map(v => Map((key, v)))
+ }(s, f)
+
+ override def getAll(keys: SortedSet[K])(s: Map[K, V] => Unit, f: Throwable => Unit): Unit =
+ Try(wrapped.getAll(keys.asJava))
+ .map(_.asScala.toMap)
+ .fold(f, s)
+
+ override def getAllAsync(keys: SortedSet[K])(s: Map[K, V] => Unit, f: Throwable => Unit): Unit =
+ withCompletion {
+ wrapped.getAllAsync(keys.asJava).asScala.map(v => v.asScala.toMap)
+ }(s, f)
+
+ override def remove(key: K)(s: Unit => Unit, f: Throwable => Unit): Unit =
+ Try(wrapped.remove(key))
+ .map(_ => ())
+ .fold(f, s)
+
+ override def removeAsync(key: K)(s: Unit => Unit, f: Throwable => Unit): Unit =
+ withCompletion {
+ wrapped.removeAsync(key).asScala.map(_ => ())
+ }(s, f)
+
+ override def removeAll(keys: SortedSet[K])(s: Unit => Unit, f: Throwable => Unit): Unit =
+ Try(wrapped.removeAll(keys.asJava))
+ .map(_ => ())
+ .fold(f, s)
+
+ override def removeAllAsync(keys: SortedSet[K])(s: Unit => Unit, f: Throwable => Unit): Unit =
+ withCompletion {
+ wrapped.removeAllAsync(keys.asJava).asScala.map(_ => ())
+ }(s, f)
+
+ override def invoke[T](key: K, entryProcessor: CacheEntryProcessor[K, V, T], arguments: Any*)(
+ s: Map[K, T] => Unit,
+ f: Throwable => Unit
+ ): Unit =
+ f(new NotImplementedError("invoke is not supported in thin client API"))
+
+ override def invokeAsync[T](key: K, entryProcessor: CacheEntryProcessor[K, V, T], arguments: Any*)(
+ s: Map[K, T] => Unit,
+ f: Throwable => Unit
+ ): Unit =
+ f(new NotImplementedError("invokeAsync is not supported in thin client API"))
+
+ override def invokeAll[T](map: SortedMap[K, CacheEntryProcessor[K, V, T]], arguments: Any*)(
+ s: Map[K, EntryProcessorResult[T]] => Unit,
+ f: Throwable => Unit
+ ): Unit =
+ f(new NotImplementedError("invokeAll is not supported in thin client API"))
+
+ override def invokeAllAsync[T](map: SortedMap[K, CacheEntryProcessor[K, V, T]], arguments: Any*)(
+ s: Map[K, EntryProcessorResult[T]] => Unit,
+ f: Throwable => Unit
+ ): Unit =
+ f(new NotImplementedError("invokeAllAsync is not supported in thin client API"))
+
+ override def lock(key: K)(s: Lock => Unit, f: Throwable => Unit): Unit =
+ f(new NotImplementedError("lock is not supported in thin client API"))
+
+ override def unlock(lock: Lock)(s: Unit => Unit, f: Throwable => Unit): Unit =
+ f(new NotImplementedError("unlock is not supported in thin client API"))
+
+ override def sql(query: SqlFieldsQuery)(s: List[List[Any]] => Unit, f: Throwable => Unit): Unit =
+ Try(
+ wrapped
+ .query(query)
+ .getAll
+ .asScala
+ .toList
+ .map(_.asScala.toList)
+ ).fold(f, s)
+
+ override def withKeepBinary(): CacheApi[K, V] = copy(wrapped = wrapped.withKeepBinary())
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/api/thin/IgniteThinApi.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/api/thin/IgniteThinApi.scala
new file mode 100644
index 00000000..2dbed496
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/api/thin/IgniteThinApi.scala
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.api.thin
+
+import scala.jdk.FutureConverters.CompletionStageOps
+import scala.util.Try
+
+import io.gatling.commons.validation.{Failure => ValidationFailure}
+import io.gatling.commons.validation.{Success => ValidationSuccess}
+import io.gatling.commons.validation.Validation
+import org.apache.ignite.binary.BinaryObjectBuilder
+import org.apache.ignite.client.ClientCacheConfiguration
+import org.apache.ignite.configuration.CacheConfiguration
+import org.apache.ignite.gatling.api.CacheApi
+import org.apache.ignite.gatling.api.IgniteApi
+import org.apache.ignite.gatling.api.TransactionApi
+import org.apache.ignite.gatling.builder.ignite.SimpleCacheConfiguration
+import org.apache.ignite.gatling.protocol.IgniteClientPool
+import org.apache.ignite.transactions.TransactionConcurrency
+import org.apache.ignite.transactions.TransactionIsolation
+import scalaz.Memo
+
+/**
+ * Implementation of IgniteApi working via the Ignite (thin) Client API.
+ *
+ * @param clientPool Pool of the IgniteClient instances.
+ */
+case class IgniteThinApi(clientPool: IgniteClientPool) extends IgniteApi with CompletionSupport {
+
+ override def cache[K, V]: String => Validation[CacheApi[K, V]] = Memo.immutableHashMapMemo[String, Validation[CacheApi[K, V]]] { name =>
+ Try(clientPool().cache[K, V](name))
+ .map(CacheThinApi[K, V])
+ .fold(ex => ValidationFailure(ex.getMessage), ValidationSuccess(_))
+ }
+
+ override def getOrCreateCacheByClientConfiguration[K, V](
+ cfg: ClientCacheConfiguration
+ )(s: CacheApi[K, V] => Unit, f: Throwable => Unit): Unit =
+ withCompletion(clientPool().getOrCreateCacheAsync[K, V](cfg).asScala.map(CacheThinApi[K, V]))(s, f)
+
+ override def getOrCreateCacheByConfiguration[K, V](
+ cfg: CacheConfiguration[K, V]
+ )(s: CacheApi[K, V] => Unit, f: Throwable => Unit): Unit =
+ f(new NotImplementedError("Node client cache configuration was used to create cache via thin client API"))
+
+ override def getOrCreateCacheBySimpleConfig[K, V](name: String, cfg: SimpleCacheConfiguration)(
+ s: CacheApi[K, V] => Unit,
+ f: Throwable => Unit
+ ): Unit =
+ getOrCreateCacheByClientConfiguration(
+ new ClientCacheConfiguration()
+ .setName(name)
+ .setCacheMode(cfg.cacheMode)
+ .setAtomicityMode(cfg.atomicity)
+ .setBackups(cfg.backups)
+ )(s, f)
+
+ override def close()(s: Unit => Unit, f: Throwable => Unit): Unit =
+ Try(clientPool().close()).fold(f, s)
+
+ override def txStart()(s: TransactionApi => Unit, f: Throwable => Unit): Unit =
+ Try(clientPool().transactions().txStart())
+ .map(TransactionThinApi)
+ .fold(f, s)
+
+ override def txStart(
+ concurrency: TransactionConcurrency,
+ isolation: TransactionIsolation
+ )(s: TransactionApi => Unit, f: Throwable => Unit): Unit =
+ Try(clientPool().transactions().txStart(concurrency, isolation))
+ .map(TransactionThinApi)
+ .fold(f, s)
+
+ override def txStart(concurrency: TransactionConcurrency, isolation: TransactionIsolation, timeout: Long, size: Int)(
+ s: TransactionApi => Unit,
+ f: Throwable => Unit
+ ): Unit =
+ Try(clientPool().transactions().txStart(concurrency, isolation, timeout))
+ .map(TransactionThinApi)
+ .fold(f, s)
+
+ override def binaryObjectBuilder: String => BinaryObjectBuilder = typeName => clientPool().binary().builder(typeName)
+
+ override def wrapped[I]: I = clientPool().asInstanceOf[I]
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/api/thin/TransactionThinApi.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/api/thin/TransactionThinApi.scala
new file mode 100644
index 00000000..31cc79be
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/api/thin/TransactionThinApi.scala
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.api.thin
+
+import scala.util.Try
+
+import org.apache.ignite.client.ClientTransaction
+import org.apache.ignite.gatling.api.TransactionApi
+
+/**
+ * Implementation of TransactionApi working via the Ignite (thin) Client API.
+ *
+ * @param wrapped Enclosed IgniteClient instance.
+ */
+case class TransactionThinApi(wrapped: ClientTransaction) extends TransactionApi {
+ override def commit(s: Unit => Unit, f: Throwable => Unit): Unit =
+ Try(wrapped.commit())
+ .fold(f, s)
+
+ override def rollback(s: Unit => Unit, f: Throwable => Unit): Unit =
+ Try(wrapped.rollback())
+ .fold(f, s)
+
+ override def close(s: Unit => Unit, f: Throwable => Unit): Unit =
+ Try(wrapped.close())
+ .fold(f, s)
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/api/thin/package.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/api/thin/package.scala
new file mode 100644
index 00000000..e69abd26
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/api/thin/package.scala
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.api
+
+import scala.concurrent.ExecutionContext
+import scala.concurrent.Future
+import scala.util.Failure
+import scala.util.Success
+
+package object thin {
+ /** Execution context to run callbacks. */
+ implicit val Ec: ExecutionContext = scala.concurrent.ExecutionContext.global
+
+ /**
+ * Helper to execute future with the callback functions passed.
+ */
+ trait CompletionSupport {
+ /**
+ * @param fut Future to execute.
+ * @param s Function to be called if future is competed successfully.
+ * @param f Function to be called if exception occurs.
+ * @tparam T Type of the future result.
+ */
+ def withCompletion[T](fut: Future[T])(s: T => Unit, f: Throwable => Unit): Unit = fut.onComplete {
+ case Success(value) => s(value)
+
+ case Failure(exception) => f(exception)
+ }
+ }
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/Cache.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/Cache.scala
new file mode 100644
index 00000000..34fc3c57
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/Cache.scala
@@ -0,0 +1,266 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.builder.cache
+
+import java.util.concurrent.locks.Lock
+
+import javax.cache.processor.MutableEntry
+
+import scala.collection.SortedMap
+import scala.collection.SortedSet
+
+import io.gatling.core.session.Expression
+import org.apache.ignite.cache.CacheEntryProcessor
+
+/**
+ * DSL to create cache operations.
+ */
+trait Cache {
+ /**
+ * Start constructing of the cache put action.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @return PutBuilder
+ */
+ def put[K, V]: PutBuilder[K, V] = PutBuilder()
+
+ /**
+ * Helper cache put action builder.
+ *
+ * Supports two flavours of configuration. The first takes two separate parameters for key and value, another one
+ * accepts a 2-tuple containing both key and pair.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ */
+ case class PutBuilder[K, V]() {
+
+ /**
+ * Starts cache put action constructing accepting separate key and value parameters.
+ *
+ * @param cacheName Cache name.
+ * @param key The cache entry key.
+ * @param value The cache entry value.
+ * @return ActionBuilder
+ */
+ def apply(cacheName: String, key: Expression[K], value: Expression[V]): CachePutActionBuilder[K, V] =
+ new CachePutActionBuilder(cacheName, session => key(session).flatMap(k => value(session).map(v => (k, v))))
+
+ /**
+ * Starts put action constructing accepting a single tuple parameter containing both key and value.
+ *
+ * @param cacheName Cache name.
+ * @param pair Tuple containing both cache entry key and value.
+ * @return ActionBuilder
+ */
+ def apply(cacheName: String, pair: Expression[(K, V)]): CachePutActionBuilder[K, V] =
+ new CachePutActionBuilder(cacheName, pair)
+ }
+
+ /**
+ * Starts constructing of the cache get action.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @param cacheName Cache name.
+ * @param key The cache entry key.
+ * @return ActionBuilder
+ */
+ def get[K, V](cacheName: String, key: Expression[K]): CacheGetActionBuilder[K, V] =
+ new CacheGetActionBuilder(cacheName, key)
+
+ /**
+ * Starts constructing of the SQL query action.
+ *
+ * @param cacheName Cache name.
+ * @param query SQL query string.
+ * @return ActionBuilder
+ */
+ def sql(cacheName: String, query: Expression[String]): CacheSqlActionBuilder =
+ CacheSqlActionBuilder(cacheName, query)
+
+ /**
+ * Starts constructing of the cache putAll action.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @param cacheName Cache name.
+ * @param map Collection of the cache entry keys and values.
+ * @return ActionBuilder
+ */
+ def putAll[K, V](cacheName: String, map: Expression[SortedMap[K, V]]): CachePutAllActionBuilder[K, V] =
+ new CachePutAllActionBuilder(cacheName, map)
+
+ /**
+ * Starts constructing of the cache getAndRemove action.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @param cacheName Cache name.
+ * @param key The cache entry key.
+ * @return ActionBuilder
+ */
+ def getAndRemove[K, V](cacheName: String, key: Expression[K]): CacheGetAndRemoveActionBuilder[K, V] =
+ new CacheGetAndRemoveActionBuilder(cacheName, key)
+
+ /**
+ * Starts constructing of the cache getAndRemove action.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @param cacheName Cache name.
+ * @param key The cache entry key.
+ * @param value The cache entry value.
+ * @return ActionBuilder
+ */
+ def getAndPut[K, V](cacheName: String, key: Expression[K], value: Expression[V]): CacheGetAndPutActionBuilder[K, V] =
+ new CacheGetAndPutActionBuilder(cacheName, key, value)
+
+ /**
+ * Starts constructing of the cache getAll action.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @param cacheName Cache name.
+ * @param keys Collection of the cache entry keys.
+ * @return ActionBuilder
+ */
+ def getAll[K, V](cacheName: String, keys: Expression[SortedSet[K]]): CacheGetAllActionBuilder[K, V] =
+ new CacheGetAllActionBuilder(cacheName, keys)
+
+ /**
+ * Starts constructing of the cache remove action.
+ *
+ * @tparam K Type of the cache key.
+ * @param cacheName Cache name.
+ * @param key The cache entry key.
+ * @return ActionBuilder
+ */
+ def remove[K](cacheName: String, key: Expression[K]): CacheRemoveActionBuilder[K] =
+ new CacheRemoveActionBuilder(cacheName, key)
+
+ /**
+ * Starts constructing of the cache removeAll action.
+ *
+ * @tparam K Type of the cache key.
+ * @param cacheName Cache name.
+ * @param keys Collection of the cache entry keys.
+ * @return ActionBuilder
+ */
+ def removeAll[K](cacheName: String, keys: Expression[SortedSet[K]]): CacheRemoveAllActionBuilder[K] =
+ new CacheRemoveAllActionBuilder(cacheName, keys)
+
+ /**
+ * Starts constructing of the cache entry processor invoke action.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @tparam T Type of the cache entry processor result.
+ * @param cacheName Cache name.
+ * @param key The cache entry key.
+ * @return ActionBuilder
+ */
+ def invoke[K, V, T](cacheName: String, key: Expression[K]): CacheInvokeActionBuilderBase[K, V, T] =
+ CacheInvokeActionBuilderBase[K, V, T](cacheName, key)
+
+ /**
+ * Starts constructing of the cache invokeAll action. This is a variant with the same
+ * entry processor instance for all keys passed.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @tparam T Type of the cache entry processor result.
+ * @param cacheName Cache name.
+ * @param keys Collection of the cache entry keys.
+ * @return ActionBuilder
+ */
+ def invokeAll[K, V, T](
+ cacheName: String,
+ keys: Expression[SortedSet[K]]
+ ): CacheInvokeAllSingleProcessorActionBuilderBase[K, V, T] =
+ CacheInvokeAllSingleProcessorActionBuilderBase[K, V, T](cacheName, keys)
+
+ /**
+ * Starts constructing of the cache invokeAll action. This is a variant with the individual
+ * entry processor instance for each keys passed.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @tparam T Type of the cache entry processor result.
+ * @param cacheName Cache name.
+ * @param map Collection of the cache entry keys and the corresponding entry processor instances.
+ * @return ActionBuilder
+ */
+ def invokeAll[K, V, T](
+ cacheName: String,
+ map: Expression[SortedMap[K, CacheEntryProcessor[K, V, T]]]
+ ): CacheInvokeAllMultipleProcessorsActionBuilder[K, V, T] =
+ new CacheInvokeAllMultipleProcessorsActionBuilder[K, V, T](cacheName, map)
+
+ /**
+ * Starts constructing of the cache entry lock action.
+ *
+ * @tparam K Type of the cache key.
+ * @param cacheName Cache name.
+ * @param key The cache entry key.
+ * @return ActionBuilder
+ */
+ def lock[K](cacheName: String, key: Expression[K]): CacheLockActionBuilder[K] =
+ new CacheLockActionBuilder(cacheName, key)
+
+ /**
+ * Starts constructing of the cache entry unlock action.
+ *
+ * @param cacheName Cache name.
+ * @param lock The lock instance to release.
+ * @return ActionBuilder
+ */
+ def unlock(cacheName: String, lock: Expression[Lock]): CacheUnlockActionBuilder =
+ new CacheUnlockActionBuilder(cacheName, lock)
+
+ /**
+ * Implicit conversion to support lambda usage in place of CacheEntryProcessor for invoke and invokeAll actions
+ * with additional arguments.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @tparam T Type of the cache entry processor result.
+ * @param f Function to be used as a cache entry processor.
+ * @return CacheEntryProcessor instance.
+ */
+ implicit def toCacheEntryProcessor[K, V, T](f: (MutableEntry[K, V], Seq[Any]) => T): CacheEntryProcessor[K, V, T] =
+ new CacheEntryProcessor[K, V, T] {
+ override def process(mutableEntry: MutableEntry[K, V], objects: Object*): T =
+ f.apply(mutableEntry, objects)
+ }
+
+ /**
+ * Implicit conversion to support lambda usage in place of CacheEntryProcessor for invoke and invokeAll actions.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @tparam T Type of the cache entry processor result.
+ * @param f Function to be used as a cache entry processor.
+ * @return CacheEntryProcessor instance.
+ */
+ implicit def toCacheEntryProcessor[K, V, T](f: MutableEntry[K, V] => T): CacheEntryProcessor[K, V, T] =
+ new CacheEntryProcessor[K, V, T] {
+ override def process(mutableEntry: MutableEntry[K, V], objects: Object*): T =
+ f.apply(mutableEntry)
+ }
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheActionParameters.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheActionParameters.scala
new file mode 100644
index 00000000..75072c64
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheActionParameters.scala
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.builder.cache
+
+import org.apache.ignite.gatling.Predef.IgniteCheck
+
+/**
+ * DSL operations for collection of common cache action parameters.
+ */
+trait CacheActionCommonParameters {
+ /** Request name. */
+ var requestName: String = ""
+
+ /** True if it should operate with binary objects. */
+ var withKeepBinary: Boolean = false
+
+ /** True if it should use async variant of API. */
+ var withAsync: Boolean = false
+
+ /**
+ * Specify whether it should operate with binary objects.
+ *
+ * @return itself.
+ */
+ def keepBinary: this.type = {
+ withKeepBinary = true
+ this
+ }
+
+ /**
+ * Specify whether it should use the async Ignite API.
+ *
+ * @return itself.
+ */
+ def async: this.type = {
+ withAsync = true
+ this
+ }
+
+ /**
+ * Specify request name for action.
+ *
+ * @param requestName Request name.
+ * @return itself.
+ */
+ def as(requestName: String): this.type = {
+ this.requestName = requestName
+
+ this
+ }
+}
+
+/**
+ * DSL operations for collection of checks for cache action.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ */
+trait CheckParameters[K, V] {
+ /** Collection of check to be performed on the query result. */
+ var checks: Seq[IgniteCheck[K, V]] = Seq.empty
+
+ /**
+ * Specify collection of check to be performed on the query result.
+ *
+ * @param newChecks collection of check to be performed on the query result.
+ * @return itself.
+ */
+ def check(newChecks: IgniteCheck[K, V]*): this.type = {
+ checks = newChecks
+ this
+ }
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheGetActionBuilder.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheGetActionBuilder.scala
new file mode 100644
index 00000000..21ea5355
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheGetActionBuilder.scala
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.builder.cache
+
+import io.gatling.core.action.Action
+import io.gatling.core.action.builder.ActionBuilder
+import io.gatling.core.session.Expression
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.gatling.action.cache.CacheGetAction
+
+/**
+ * Cache entry get action builder.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @param cacheName Cache name.
+ * @param key The cache entry key.
+ */
+class CacheGetActionBuilder[K, V](
+ cacheName: String,
+ key: Expression[K]
+) extends ActionBuilder
+ with CacheActionCommonParameters
+ with CheckParameters[K, V] {
+
+ /**
+ * Builds an action.
+ *
+ * @param ctx The scenario context.
+ * @param next The action that will be chained with the Action build by this builder.
+ * @return The resulting action.
+ */
+ override def build(ctx: ScenarioContext, next: Action): Action =
+ new CacheGetAction(requestName, cacheName, key, withKeepBinary, withAsync, checks, next, ctx)
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheGetAllActionBuilder.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheGetAllActionBuilder.scala
new file mode 100644
index 00000000..ec685187
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheGetAllActionBuilder.scala
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.builder.cache
+
+import scala.collection.SortedSet
+
+import io.gatling.core.action.Action
+import io.gatling.core.action.builder.ActionBuilder
+import io.gatling.core.session.Expression
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.gatling.action.cache.CacheGetAllAction
+
+/**
+ * Cache getAll action builder.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @param cacheName Cache name.
+ * @param keys The collection of the cache entry keys.
+ */
+class CacheGetAllActionBuilder[K, V](
+ cacheName: String,
+ keys: Expression[SortedSet[K]]
+) extends ActionBuilder
+ with CacheActionCommonParameters
+ with CheckParameters[K, V] {
+
+ /**
+ * Builds an action.
+ *
+ * @param ctx The scenario context.
+ * @param next The action that will be chained with the Action build by this builder.
+ * @return The resulting action.
+ */
+ override def build(ctx: ScenarioContext, next: Action): Action =
+ new CacheGetAllAction(requestName, cacheName, keys, withKeepBinary, withAsync, checks, next, ctx)
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheGetAndPutActionBuilder.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheGetAndPutActionBuilder.scala
new file mode 100644
index 00000000..d6ada8d1
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheGetAndPutActionBuilder.scala
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.builder.cache
+
+import io.gatling.core.action.Action
+import io.gatling.core.action.builder.ActionBuilder
+import io.gatling.core.session.Expression
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.gatling.action.cache.CacheGetAndPutAction
+
+/**
+ * Cache entry getAndPut action builder.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @param cacheName Cache name.
+ * @param key The cache entry key.
+ * @param value The cache entry value.
+ */
+class CacheGetAndPutActionBuilder[K, V](
+ cacheName: String,
+ key: Expression[K],
+ value: Expression[V]
+) extends ActionBuilder
+ with CacheActionCommonParameters
+ with CheckParameters[K, V] {
+
+ /**
+ * Builds an action.
+ *
+ * @param ctx The scenario context.
+ * @param next The action that will be chained with the Action build by this builder.
+ * @return The resulting action.
+ */
+ override def build(ctx: ScenarioContext, next: Action): Action =
+ new CacheGetAndPutAction(requestName, cacheName, key, value, withKeepBinary, withAsync, checks, next, ctx)
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheGetAndRemoveActionBuilder.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheGetAndRemoveActionBuilder.scala
new file mode 100644
index 00000000..b4ccf3df
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheGetAndRemoveActionBuilder.scala
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.builder.cache
+
+import io.gatling.core.action.Action
+import io.gatling.core.action.builder.ActionBuilder
+import io.gatling.core.session.Expression
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.gatling.action.cache.CacheGetAndRemoveAction
+
+/**
+ * Cache entry getAndRemove action builder.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @param cacheName Cache name.
+ * @param key The cache entry key.
+ */
+class CacheGetAndRemoveActionBuilder[K, V](
+ cacheName: String,
+ key: Expression[K]
+) extends ActionBuilder
+ with CacheActionCommonParameters
+ with CheckParameters[K, V] {
+
+ /**
+ * Builds an action.
+ *
+ * @param ctx The scenario context.
+ * @param next The action that will be chained with the Action build by this builder.
+ * @return The resulting action.
+ */
+ override def build(ctx: ScenarioContext, next: Action): Action =
+ new CacheGetAndRemoveAction(requestName, cacheName, key, withKeepBinary, withAsync, checks, next, ctx)
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheInvokeActionBuilder.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheInvokeActionBuilder.scala
new file mode 100644
index 00000000..0af7779c
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheInvokeActionBuilder.scala
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.builder.cache
+
+import io.gatling.core.action.Action
+import io.gatling.core.action.builder.ActionBuilder
+import io.gatling.core.session.Expression
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.cache.CacheEntryProcessor
+import org.apache.ignite.gatling.action.cache.CacheInvokeAction
+
+/**
+ * Base invoke action builder.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @tparam T Type of the entry processor result.
+ * @param cacheName Cache name.
+ * @param key The cache entry key to run entry processor for.
+ */
+case class CacheInvokeActionBuilderBase[K, V, T](
+ cacheName: String,
+ key: Expression[K]
+) {
+ /**
+ * Specify the entry processor instance bypassing the additional arguments step.
+ *
+ * @param entryProcessor Entry processor instance. May be lambda.
+ * @return Builder step for common cache action parameters.
+ */
+ def apply(entryProcessor: CacheEntryProcessor[K, V, T]): CacheInvokeActionBuilder[K, V, T] =
+ CacheInvokeActionBuilderProcessorStep(cacheName, key, Seq.empty)(entryProcessor)
+
+ /**
+ * Specify additional arguments to pass to the entry processor.
+ *
+ * @param args Additional arguments to pass to the entry processor.
+ * @return Builder step for entry processor.
+ */
+ def args(args: Expression[Any]*): CacheInvokeActionBuilderProcessorStep[K, V, T] =
+ CacheInvokeActionBuilderProcessorStep[K, V, T](cacheName, key, args)
+}
+
+/**
+ * Builder step for entry processor instance specification.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @tparam T Type of the entry processor result.
+ * @param cacheName Cache name.
+ * @param key The cache entry key to run entry processor for.
+ * @param args Additional arguments to pass to the entry processor.
+ */
+case class CacheInvokeActionBuilderProcessorStep[K, V, T](
+ cacheName: String,
+ key: Expression[K],
+ args: Seq[Expression[Any]]
+) {
+ /**
+ * Specify the entry processor instance.
+ *
+ * @param entryProcessor Entry processor instance. May be lambda.
+ * @return Builder step for common cache action parameters.
+ */
+ def apply(entryProcessor: CacheEntryProcessor[K, V, T]): CacheInvokeActionBuilder[K, V, T] =
+ new CacheInvokeActionBuilder[K, V, T](cacheName, key, entryProcessor, args)
+}
+
+/**
+ * Builder step for common cache action parameters.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @tparam T Type of the entry processor result.
+ * @param cacheName Cache name.
+ * @param key The cache entry key.
+ * @param entryProcessor Instance of CacheEntryProcessor.
+ * @param arguments Additional arguments to pass to the entry processor.
+ */
+class CacheInvokeActionBuilder[K, V, T](
+ cacheName: String,
+ key: Expression[K],
+ entryProcessor: CacheEntryProcessor[K, V, T],
+ arguments: Seq[Expression[Any]]
+) extends ActionBuilder
+ with CacheActionCommonParameters
+ with CheckParameters[K, T] {
+
+ /**
+ * Builds an action.
+ *
+ * @param ctx The scenario context.
+ * @param next The action that will be chained with the Action build by this builder.
+ * @return The resulting action.
+ */
+ override def build(ctx: ScenarioContext, next: Action): Action =
+ new CacheInvokeAction[K, V, T](
+ requestName,
+ cacheName,
+ key,
+ entryProcessor,
+ arguments,
+ withKeepBinary,
+ withAsync,
+ checks,
+ next,
+ ctx
+ )
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheInvokeAllMultipleProcessorsActionBuilder.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheInvokeAllMultipleProcessorsActionBuilder.scala
new file mode 100644
index 00000000..3a74e72e
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheInvokeAllMultipleProcessorsActionBuilder.scala
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.builder.cache
+
+import javax.cache.processor.EntryProcessorResult
+
+import scala.collection.SortedMap
+
+import io.gatling.core.action.Action
+import io.gatling.core.action.builder.ActionBuilder
+import io.gatling.core.session.Expression
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.cache.CacheEntryProcessor
+import org.apache.ignite.gatling.action.cache.CacheInvokeAllMapAction
+
+/**
+ * Base invokeAll action builder.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @tparam T Type of the entry processor result.
+ * @param cacheName Cache name.
+ * @param map Map from cache entry key to CacheEntryProcessor to invoke for this particular entry.
+ */
+class CacheInvokeAllMultipleProcessorsActionBuilder[K, V, T](
+ cacheName: String,
+ map: Expression[SortedMap[K, CacheEntryProcessor[K, V, T]]]
+) extends ActionBuilder
+ with CacheActionCommonParameters
+ with CheckParameters[K, EntryProcessorResult[T]] {
+
+ /** Additional arguments to pass to the entry processor. */
+ var arguments: Seq[Expression[Any]] = Seq.empty
+
+ /**
+ * Builds an action.
+ *
+ * @param ctx The scenario context.
+ * @param next The action that will be chained with the Action build by this builder.
+ * @return The resulting action.
+ */
+ override def build(ctx: ScenarioContext, next: Action): Action =
+ new CacheInvokeAllMapAction[K, V, T](
+ requestName,
+ cacheName,
+ map,
+ arguments,
+ withKeepBinary,
+ withAsync,
+ Seq.empty,
+ next,
+ ctx
+ )
+
+ /**
+ * Specify additional arguments to pass to the entry processor.
+ *
+ * @param args Additional arguments to pass to the entry processor.
+ * @return itself.
+ */
+ def args(args: Expression[Any]*): CacheInvokeAllMultipleProcessorsActionBuilder[K, V, T] = {
+ arguments = args
+ this
+ }
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheInvokeAllSingleProcessorActionBuilder.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheInvokeAllSingleProcessorActionBuilder.scala
new file mode 100644
index 00000000..d8f083d9
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheInvokeAllSingleProcessorActionBuilder.scala
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.builder.cache
+
+import javax.cache.processor.EntryProcessorResult
+
+import scala.collection.SortedSet
+
+import io.gatling.core.action.Action
+import io.gatling.core.action.builder.ActionBuilder
+import io.gatling.core.session.Expression
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.cache.CacheEntryProcessor
+import org.apache.ignite.gatling.action.cache.CacheInvokeAllSingleProcessorAction
+
+/**
+ * Base invokeAll action builder.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @tparam T Type of the entry processor result.
+ * @param cacheName Cache name.
+ * @param keys Collection of the cache keys to run entry processor for.
+ */
+case class CacheInvokeAllSingleProcessorActionBuilderBase[K, V, T](
+ cacheName: String,
+ keys: Expression[SortedSet[K]]
+) {
+ /**
+ * Specify the entry processor instance bypassing the additional arguments step.
+ *
+ * @param entryProcessor Entry processor instance. May be lambda.
+ * @return Builder step for common cache action parameters.
+ */
+ def apply(entryProcessor: CacheEntryProcessor[K, V, T]): CacheInvokeAllSingleProcessorActionBuilder[K, V, T] =
+ CacheInvokeAllActionBuilderEntryProcessorStep(cacheName, keys, Seq.empty)(entryProcessor)
+
+ /**
+ * Specify additional arguments to pass to the entry processor.
+ *
+ * @param args Additional arguments to pass to the entry processor.
+ * @return Builder step for entry processor.
+ */
+ def args(args: Expression[Any]*): CacheInvokeAllActionBuilderEntryProcessorStep[K, V, T] =
+ CacheInvokeAllActionBuilderEntryProcessorStep[K, V, T](cacheName, keys, args)
+}
+
+/**
+ * Builder step for entry processor instance specification.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @tparam T Type of the entry processor result.
+ * @param cacheName Cache name.
+ * @param keys Collection of the cache keys to run entry processor for.
+ * @param args Additional arguments to pass to the entry processor.
+ */
+case class CacheInvokeAllActionBuilderEntryProcessorStep[K, V, T](
+ cacheName: String,
+ keys: Expression[SortedSet[K]],
+ args: Seq[Expression[Any]]
+) {
+ /**
+ * Specify the entry processor instance.
+ *
+ * @param entryProcessor Entry processor instance. May be lambda.
+ * @return Builder step for common cache action parameters.
+ */
+ def apply(entryProcessor: CacheEntryProcessor[K, V, T]): CacheInvokeAllSingleProcessorActionBuilder[K, V, T] =
+ new CacheInvokeAllSingleProcessorActionBuilder[K, V, T](cacheName, keys, entryProcessor, args)
+}
+
+/**
+ * Builder step for common cache action parameters.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @tparam T Type of the entry processor result.
+ * @param cacheName Cache name.
+ * @param keys Collection of the cache keys to run entry processor for.
+ * @param args Additional arguments to pass to the entry processor.
+ * @param entryProcessor Entry processor instance. May be lambda.
+ */
+class CacheInvokeAllSingleProcessorActionBuilder[K, V, T](
+ cacheName: String,
+ keys: Expression[SortedSet[K]],
+ entryProcessor: CacheEntryProcessor[K, V, T],
+ args: Seq[Expression[Any]]
+) extends ActionBuilder
+ with CacheActionCommonParameters
+ with CheckParameters[K, EntryProcessorResult[T]] {
+
+ /**
+ * Builds an action.
+ *
+ * @param ctx The scenario context.
+ * @param next The action that will be chained with the Action build by this builder.
+ * @return The resulting action.
+ */
+ override def build(ctx: ScenarioContext, next: Action): Action =
+ new CacheInvokeAllSingleProcessorAction[K, V, T](
+ requestName,
+ cacheName,
+ keys,
+ entryProcessor,
+ args,
+ withKeepBinary,
+ withAsync,
+ checks,
+ next,
+ ctx
+ )
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheLockActionBuilder.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheLockActionBuilder.scala
new file mode 100644
index 00000000..106a2186
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheLockActionBuilder.scala
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.builder.cache
+
+import java.util.concurrent.locks.Lock
+
+import io.gatling.core.action.Action
+import io.gatling.core.action.builder.ActionBuilder
+import io.gatling.core.session.Expression
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.gatling.action.cache.CacheLockAction
+
+/**
+ * Cache entry obtain lock action builder.
+ *
+ * @tparam K Type of the cache key.
+ * @param cacheName Cache name.
+ * @param key The cache entry key.
+ */
+class CacheLockActionBuilder[K](
+ cacheName: String,
+ key: Expression[K]
+) extends ActionBuilder
+ with CacheActionCommonParameters
+ with CheckParameters[K, Lock] {
+
+ /**
+ * Builds an action.
+ *
+ * @param ctx The scenario context.
+ * @param next The action that will be chained with the Action build by this builder.
+ * @return The resulting action.
+ */
+ override def build(ctx: ScenarioContext, next: Action): Action =
+ new CacheLockAction(requestName, cacheName, key, keepBinary = withKeepBinary, checks, next, ctx)
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CachePutActionBuilder.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CachePutActionBuilder.scala
new file mode 100644
index 00000000..31410c1d
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CachePutActionBuilder.scala
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.builder.cache
+
+import io.gatling.core.action.Action
+import io.gatling.core.action.builder.ActionBuilder
+import io.gatling.core.session.Expression
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.gatling.action.cache.CachePutAction
+
+/**
+ * Cache entry put action builder.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @param cacheName Cache name.
+ * @param pair The cache entry key.
+ */
+class CachePutActionBuilder[K, V](
+ cacheName: String,
+ pair: Expression[(K, V)]
+) extends ActionBuilder
+ with CacheActionCommonParameters {
+
+ /**
+ * Builds an action.
+ *
+ * @param ctx The scenario context.
+ * @param next The action that will be chained with the Action build by this builder.
+ * @return The resulting action.
+ */
+ override def build(ctx: ScenarioContext, next: Action): Action =
+ new CachePutAction(requestName, cacheName, pair, withKeepBinary, withAsync, next, ctx)
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CachePutAllActionBuilder.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CachePutAllActionBuilder.scala
new file mode 100644
index 00000000..a2c66f94
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CachePutAllActionBuilder.scala
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.builder.cache
+
+import scala.collection.SortedMap
+
+import io.gatling.core.action.Action
+import io.gatling.core.action.builder.ActionBuilder
+import io.gatling.core.session.Expression
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.gatling.action.cache.CachePutAllAction
+
+/**
+ * Cache putAll action builder.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @param cacheName Cache name.
+ * @param map Collection of cache entry keys and values.
+ */
+class CachePutAllActionBuilder[K, V](
+ cacheName: String,
+ map: Expression[SortedMap[K, V]]
+) extends ActionBuilder
+ with CacheActionCommonParameters {
+
+ /**
+ * Builds an action.
+ *
+ * @param ctx The scenario context.
+ * @param next The action that will be chained with the Action build by this builder.
+ * @return The resulting action.
+ */
+ override def build(ctx: ScenarioContext, next: Action): Action =
+ new CachePutAllAction(requestName, cacheName, map, withKeepBinary, withAsync, next, ctx)
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheRemoveActionBuilder.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheRemoveActionBuilder.scala
new file mode 100644
index 00000000..6f55f89e
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheRemoveActionBuilder.scala
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.builder.cache
+
+import io.gatling.core.action.Action
+import io.gatling.core.action.builder.ActionBuilder
+import io.gatling.core.session.Expression
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.gatling.action.cache.CacheRemoveAction
+
+/**
+ * Cache entry remove action builder.
+ *
+ * @tparam K Type of the cache key.
+ * @param cacheName Cache name.
+ * @param key The cache entry key.
+ */
+class CacheRemoveActionBuilder[K](
+ cacheName: String,
+ key: Expression[K]
+) extends ActionBuilder
+ with CacheActionCommonParameters {
+
+ /**
+ * Builds an action.
+ *
+ * @param ctx The scenario context.
+ * @param next The action that will be chained with the Action build by this builder.
+ * @return The resulting action.
+ */
+ override def build(ctx: ScenarioContext, next: Action): Action =
+ new CacheRemoveAction(requestName, cacheName, key, withKeepBinary, withAsync, next, ctx)
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheRemoveAllActionBuilder.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheRemoveAllActionBuilder.scala
new file mode 100644
index 00000000..05492e32
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheRemoveAllActionBuilder.scala
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.builder.cache
+
+import scala.collection.SortedSet
+
+import io.gatling.core.action.Action
+import io.gatling.core.action.builder.ActionBuilder
+import io.gatling.core.session.Expression
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.gatling.action.cache.CacheRemoveAllAction
+
+/**
+ * Cache entry remove action builder.
+ *
+ * @tparam K Type of the cache key.
+ * @param cacheName Cache name.
+ * @param keys Collection of cache entry keys.
+ */
+class CacheRemoveAllActionBuilder[K](
+ cacheName: String,
+ keys: Expression[SortedSet[K]]
+) extends ActionBuilder
+ with CacheActionCommonParameters {
+
+ /**
+ * Builds an action.
+ *
+ * @param ctx The scenario context.
+ * @param next The action that will be chained with the Action build by this builder.
+ * @return The resulting action.
+ */
+ override def build(ctx: ScenarioContext, next: Action): Action =
+ new CacheRemoveAllAction(requestName, cacheName, keys, withKeepBinary, withAsync, next, ctx)
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheSqlActionBuilder.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheSqlActionBuilder.scala
new file mode 100644
index 00000000..7ed8fcd9
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheSqlActionBuilder.scala
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.builder.cache
+
+import io.gatling.commons.validation.SuccessWrapper
+import io.gatling.core.action.Action
+import io.gatling.core.action.builder.ActionBuilder
+import io.gatling.core.session.Expression
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.gatling.Predef.SqlCheck
+import org.apache.ignite.gatling.action.cache.CacheSqlAction
+
+/**
+ * SQL query action builder.
+ *
+ * @param cacheName Cache name.
+ * @param sql SQL query.
+ * @param argsList Optional list of the positional query parameters.
+ * @param partitionsList Optional list of cache partitions to execute query on.
+ * @param checks Optional collection of check to be performed on the query result.
+ * @param requestName Request name.
+ */
+case class CacheSqlActionBuilder(
+ cacheName: String,
+ sql: Expression[String],
+ argsList: List[Expression[Any]] = List.empty,
+ partitionsList: Expression[List[Int]] = _ => List.empty.success,
+ checks: Seq[SqlCheck] = Seq.empty,
+ requestName: String = ""
+) extends ActionBuilder {
+
+ /**
+ * Specify collection of check to be performed on the query result.
+ *
+ * @param newChecks collection of check to be performed on the query result.
+ * @return itself.
+ */
+ def check(newChecks: SqlCheck*): CacheSqlActionBuilder = this.copy(checks = newChecks)
+
+ /**
+ * Specify list of the positional query parameters.
+ *
+ * @param newArgs list of the positional query parameters.
+ * @return itself.
+ */
+ def args(newArgs: Expression[Any]*): CacheSqlActionBuilder = this.copy(argsList = newArgs.toList)
+
+ /**
+ * Specify list of partitions.
+ *
+ * @param newPartitions list of cache partitions to execute query on.
+ * @return itself.
+ */
+ def partitions(newPartitions: Expression[List[Int]]): CacheSqlActionBuilder = this.copy(partitionsList = newPartitions)
+
+ /**
+ * Specify request name for action.
+ *
+ * @param requestName Request name.
+ * @return itself.
+ */
+ def as(requestName: String): ActionBuilder = this.copy(requestName = requestName)
+
+ /**
+ * Builds an action.
+ *
+ * @param ctx The scenario context.
+ * @param next The action that will be chained with the Action build by this builder.
+ * @return The resulting action.
+ */
+ override def build(ctx: ScenarioContext, next: Action): Action =
+ new CacheSqlAction(requestName, cacheName, sql, argsList, partitionsList, keepBinary = false, checks, next, ctx)
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheUnlockActionBuilder.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheUnlockActionBuilder.scala
new file mode 100644
index 00000000..429a5d51
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/cache/CacheUnlockActionBuilder.scala
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.builder.cache
+
+import java.util.concurrent.locks.Lock
+
+import io.gatling.core.action.Action
+import io.gatling.core.action.builder.ActionBuilder
+import io.gatling.core.session.Expression
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.gatling.action.cache.CacheUnlockAction
+
+/**
+ * Cache entry release lock action builder.
+ *
+ * @param cacheName Cache name.
+ * @param lock Lock instance.
+ */
+class CacheUnlockActionBuilder(
+ cacheName: String,
+ lock: Expression[Lock]
+) extends ActionBuilder
+ with CacheActionCommonParameters {
+
+ /**
+ * Builds an action.
+ *
+ * @param ctx The scenario context.
+ * @param next The action that will be chained with the Action build by this builder.
+ * @return The resulting action.
+ */
+ override def build(ctx: ScenarioContext, next: Action): Action =
+ new CacheUnlockAction(requestName, cacheName, lock, keepBinary = withKeepBinary, next, ctx)
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/ignite/CloseClientActionBuilder.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/ignite/CloseClientActionBuilder.scala
new file mode 100644
index 00000000..2190c826
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/ignite/CloseClientActionBuilder.scala
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.builder.ignite
+
+import io.gatling.core.action.Action
+import io.gatling.core.action.builder.ActionBuilder
+import io.gatling.core.session.EmptyStringExpressionSuccess
+import io.gatling.core.session.Expression
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.gatling.action.ignite.CloseClientAction
+
+/**
+ * Close Ignite API instance action builder.
+ *
+ * @param requestName Request name.
+ */
+case class CloseClientActionBuilder(requestName: String = "") extends ActionBuilder {
+ /**
+ * Specify request name for action.
+ *
+ * @param requestName Request name.
+ * @return itself.
+ */
+ def as(requestName: String): ActionBuilder = this.copy(requestName = requestName)
+
+ /**
+ * Builds an action.
+ *
+ * @param ctx The scenario context.
+ * @param next The action that will be chained with the Action build by this builder.
+ * @return The resulting action.
+ */
+ override def build(ctx: ScenarioContext, next: Action): Action =
+ new CloseClientAction(requestName, next, ctx)
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/ignite/GetOrCreateCacheActionBuilder.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/ignite/GetOrCreateCacheActionBuilder.scala
new file mode 100644
index 00000000..9f1a9d0d
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/ignite/GetOrCreateCacheActionBuilder.scala
@@ -0,0 +1,234 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.builder.ignite
+
+import io.gatling.core.action.Action
+import io.gatling.core.action.builder.ActionBuilder
+import io.gatling.core.session.EmptyStringExpressionSuccess
+import io.gatling.core.session.Expression
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.cache.CacheAtomicityMode
+import org.apache.ignite.cache.CacheMode
+import org.apache.ignite.client.ClientCacheConfiguration
+import org.apache.ignite.configuration.CacheConfiguration
+import org.apache.ignite.gatling.action.ignite.GetOrCreateCacheAction
+
+/**
+ * Base get or create cache action builder.
+ *
+ * Works in two modes. The simplified one allows to specify three basic parameters via the DSL (backups, atomicity and mode).
+ * Other way the full fledged instances of ClientCacheConfiguration or CacheConfiguration may be passed via the `cfg`
+ * method.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @param cacheName Cache name.
+ */
+case class GetOrCreateCacheActionBuilderBase[K, V](cacheName: String) extends ActionBuilder {
+ /**
+ * Specify number of backup copies.
+ *
+ * @param backups Number of backup copies.
+ * @return Build step to specify other cache parameters via the DSL.
+ */
+ def backups(backups: Integer): GetOrCreateCacheActionBuilderSimpleConfigStep =
+ GetOrCreateCacheActionBuilderSimpleConfigStep(cacheName, SimpleCacheConfiguration(backups = backups))
+
+ /**
+ * Specify atomicity.
+ *
+ * @param atomicity Atomicity.
+ * @return Build step to specify other cache parameters via the DSL.
+ */
+ def atomicity(atomicity: CacheAtomicityMode): GetOrCreateCacheActionBuilderSimpleConfigStep =
+ GetOrCreateCacheActionBuilderSimpleConfigStep(cacheName, SimpleCacheConfiguration(atomicity = atomicity))
+
+ /**
+ * Specify cache partitioning mode.
+ *
+ * @param cacheMode Cache partitioning mode.
+ * @return Build step to specify other cache parameters via the DSL.
+ */
+ def mode(cacheMode: CacheMode): GetOrCreateCacheActionBuilderSimpleConfigStep =
+ GetOrCreateCacheActionBuilderSimpleConfigStep(cacheName, SimpleCacheConfiguration(cacheMode = cacheMode))
+
+ /**
+ * Specify full cache configuration via the ClientCacheConfiguration instance
+ * which is part of Ignite Client (thin) API.
+ *
+ * @param clientCacheCfg Client cache configuration.
+ * @return Build step to specify other cache parameters via the DSL.
+ */
+ def cfg(clientCacheCfg: ClientCacheConfiguration): GetOrCreateCacheActionBuilder[K, V] =
+ new GetOrCreateCacheActionBuilder(cacheName, ThinConfiguration(clientCacheCfg))
+
+ /**
+ * Specify full cache configuration via the CacheConfiguration instance
+ * which is part of Ignite Node (thick) API.
+ *
+ * @param cacheCfg Cache configuration.
+ * @return Build step to specify other cache parameters via the DSL.
+ */
+ def cfg(cacheCfg: CacheConfiguration[K, V]): GetOrCreateCacheActionBuilder[K, V] =
+ new GetOrCreateCacheActionBuilder(cacheName, ThickConfiguration(cacheCfg))
+
+ /**
+ * Builds an action.
+ *
+ * @param ctx The scenario context.
+ * @param next The action that will be chained with the Action build by this builder.
+ * @return The resulting action.
+ */
+ override def build(ctx: ScenarioContext, next: Action): Action =
+ new GetOrCreateCacheActionBuilder(cacheName, SimpleCacheConfiguration()).build(ctx, next)
+
+ /**
+ * Specify request name for action.
+ *
+ * @param requestName Request name.
+ * @return itself.
+ */
+ def as(requestName: String): ActionBuilder =
+ new GetOrCreateCacheActionBuilder(cacheName, SimpleCacheConfiguration()).as(requestName)
+}
+
+/**
+ * Builder step to specify simple cache parameters via DSL.
+ *
+ * @param cacheName Cache name.
+ * @param simpleConfig Simple cache configuration instance.
+ */
+case class GetOrCreateCacheActionBuilderSimpleConfigStep(
+ cacheName: String,
+ simpleConfig: SimpleCacheConfiguration
+) extends ActionBuilder {
+ /**
+ * Specify number of backup copies.
+ *
+ * @param backups Number of backup copies.
+ * @return Build step to specify other cache parameters via the DSL.
+ */
+ def backups(backups: Integer): GetOrCreateCacheActionBuilderSimpleConfigStep =
+ this.copy(simpleConfig = simpleConfig.copy(backups = backups))
+
+ /**
+ * Specify atomicity.
+ *
+ * @param atomicity Atomicity.
+ * @return Build step to specify other cache parameters via the DSL.
+ */
+ def atomicity(atomicity: CacheAtomicityMode): GetOrCreateCacheActionBuilderSimpleConfigStep =
+ this.copy(simpleConfig = simpleConfig.copy(atomicity = atomicity))
+
+ /**
+ * Specify cache partitioning mode.
+ *
+ * @param cacheMode Cache partitioning mode.
+ * @return Build step to specify other cache parameters via the DSL.
+ */
+ def mode(cacheMode: CacheMode): GetOrCreateCacheActionBuilderSimpleConfigStep =
+ this.copy(simpleConfig = simpleConfig.copy(cacheMode = cacheMode))
+
+ /**
+ * Builds an action.
+ *
+ * @param ctx The scenario context.
+ * @param next The action that will be chained with the Action build by this builder.
+ * @return The resulting action.
+ */
+ override def build(ctx: ScenarioContext, next: Action): Action =
+ createCacheActionBuilder.build(ctx, next)
+
+ /**
+ * Specify request name for action.
+ *
+ * @param requestName Request name.
+ * @return itself.
+ */
+ def as(requestName: String): ActionBuilder =
+ createCacheActionBuilder.as(requestName)
+
+ private def createCacheActionBuilder[K, V]: GetOrCreateCacheActionBuilder[K, V] =
+ new GetOrCreateCacheActionBuilder[K, V](
+ cacheName,
+ simpleConfig
+ )
+}
+
+/**
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @param cacheName Cache name.
+ * @param config Cache configuration.
+ */
+class GetOrCreateCacheActionBuilder[K, V](cacheName: String, config: Configuration) extends ActionBuilder {
+ /** Request name. */
+ var requestName: String = ""
+
+ /**
+ * Builds an action.
+ *
+ * @param ctx The scenario context.
+ * @param next The action that will be chained with the Action build by this builder.
+ * @return The resulting action.
+ */
+ override def build(ctx: ScenarioContext, next: Action): Action =
+ new GetOrCreateCacheAction(requestName, cacheName, config, next, ctx)
+
+ /**
+ * Specify request name for action.
+ *
+ * @param requestName Request name.
+ * @return itself.
+ */
+ def as(requestName: String): GetOrCreateCacheActionBuilder[K, V] = {
+ this.requestName = requestName
+ this
+ }
+}
+
+/**
+ * Abstract cache configuration
+ */
+sealed trait Configuration
+
+/**
+ * Simplified cache configuration.
+ *
+ * @param backups Number of backup copies.
+ * @param atomicity Atomicity.
+ * @param cacheMode Cache partitioning mode.
+ */
+case class SimpleCacheConfiguration(
+ backups: Integer = 0,
+ atomicity: CacheAtomicityMode = CacheAtomicityMode.ATOMIC,
+ cacheMode: CacheMode = CacheMode.PARTITIONED
+) extends Configuration
+
+/**
+ * Ignite Client (thin) cache configuration.
+ *
+ * @param cfg ClientCacheConfiguration instance.
+ */
+case class ThinConfiguration(cfg: ClientCacheConfiguration) extends Configuration
+
+/**
+ * Ignite node (thick) cache configuration.
+ *
+ * @param cfg CacheConfiguration instance.
+ */
+case class ThickConfiguration(cfg: CacheConfiguration[_, _]) extends Configuration
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/ignite/Ignite.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/ignite/Ignite.scala
new file mode 100644
index 00000000..e483154d
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/ignite/Ignite.scala
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.builder.ignite
+
+import io.gatling.core.session.Expression
+
+/**
+ * DSL to create Ignite operations.
+ */
+trait Ignite {
+ /**
+ * Start constructing of the create cache action with the provided cache name.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ * @param cacheName Cache name.
+ * @return CreateCacheActionBuilderBase
+ */
+ def getOrCreateCache[K, V](cacheName: String): GetOrCreateCacheActionBuilderBase[K, V] =
+ GetOrCreateCacheActionBuilderBase(cacheName)
+
+ /**
+ * Start constructing of the start Ignite API action.
+ *
+ * @return StartClientActionBuilder
+ */
+ def startIgniteApi: StartClientActionBuilder = StartClientActionBuilder()
+
+ /**
+ * Start constructing of the close Ignite API action.
+ *
+ * @return CloseClientActionBuilder
+ */
+ def closeIgniteApi: CloseClientActionBuilder = CloseClientActionBuilder()
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/ignite/LambdaActionBuilder.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/ignite/LambdaActionBuilder.scala
new file mode 100644
index 00000000..9ef78f3f
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/ignite/LambdaActionBuilder.scala
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.builder.ignite
+
+import io.gatling.core.action.Action
+import io.gatling.core.action.builder.ActionBuilder
+import io.gatling.core.session.EmptyStringExpressionSuccess
+import io.gatling.core.session.Expression
+import io.gatling.core.session.Session
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.gatling.action.ignite.LambdaAction
+
+/**
+ * Lambda function action builder.
+ *
+ * @tparam I Ignite Api class.
+ * @param function Lambda function to be executed.
+ * @param requestName Request name.
+ */
+case class LambdaActionBuilder[I](
+ function: (I, Session) => Any,
+ requestName: String = ""
+) extends ActionBuilder {
+ /**
+ * Specify request name for action.
+ *
+ * @param requestName Request name.
+ * @return itself.
+ */
+ def as(requestName: String): ActionBuilder = this.copy(requestName = requestName)
+
+ override def build(ctx: ScenarioContext, next: Action): Action =
+ new LambdaAction(requestName, function, next, ctx)
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/ignite/StartClientActionBuilder.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/ignite/StartClientActionBuilder.scala
new file mode 100644
index 00000000..ca25ed64
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/ignite/StartClientActionBuilder.scala
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.builder.ignite
+
+import io.gatling.core.action.Action
+import io.gatling.core.action.builder.ActionBuilder
+import io.gatling.core.session.EmptyStringExpressionSuccess
+import io.gatling.core.session.Expression
+import io.gatling.core.structure.ScenarioContext
+import org.apache.ignite.gatling.action.ignite.StartClientAction
+
+/**
+ * Start Ignite API instance action builder.
+ *
+ * @param requestName Request name.
+ */
+case class StartClientActionBuilder(requestName: String = "") extends ActionBuilder {
+ /**
+ * Specify request name for action.
+ *
+ * @param requestName Request name.
+ * @return itself.
+ */
+ def as(requestName: String): ActionBuilder = this.copy(requestName = requestName)
+
+ /**
+ * Builds an action.
+ *
+ * @param ctx The scenario context.
+ * @param next The action that will be chained with the Action build by this builder.
+ * @return The resulting action.
+ */
+ override def build(ctx: ScenarioContext, next: Action): Action =
+ new StartClientAction(requestName, next, ctx)
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/transaction/Transactions.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/transaction/Transactions.scala
new file mode 100644
index 00000000..f8b1ee75
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/builder/transaction/Transactions.scala
@@ -0,0 +1,296 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.builder.transaction
+
+import io.gatling.commons.validation.SuccessWrapper
+import io.gatling.core.Predef.exec
+import io.gatling.core.Predef.group
+import io.gatling.core.action.Action
+import io.gatling.core.action.builder.ActionBuilder
+import io.gatling.core.structure.ChainBuilder
+import io.gatling.core.structure.ScenarioContext
+import io.gatling.core.util.NameGen
+import org.apache.ignite.gatling.IgniteDslInvalidConfigurationException
+import org.apache.ignite.gatling.action.ignite.TransactionCloseAction
+import org.apache.ignite.gatling.action.ignite.TransactionCommitAction
+import org.apache.ignite.gatling.action.ignite.TransactionRollbackAction
+import org.apache.ignite.gatling.action.ignite.TransactionStartAction
+import org.apache.ignite.gatling.builder.cache.CacheActionCommonParameters
+import org.apache.ignite.transactions.TransactionConcurrency
+import org.apache.ignite.transactions.TransactionIsolation
+
+/**
+ * DSL to create transaction operations.
+ */
+trait Transactions {
+ /**
+ * Start constructing of the transaction.
+ *
+ * @return TransactionBuilderParametersStep.
+ */
+ def tx: TransactionBuilderParametersStep = TransactionBuilderParametersStep(TransactionParameters())
+
+ /**
+ * Implicit to build transaction chain with the default request name.
+ *
+ * @param transactionBuilder: TransactionBuilder.
+ * @return Protocol.
+ */
+ implicit def transactionBuilder2ChainBuilder(transactionBuilder: TransactionBuilder): ChainBuilder =
+ transactionBuilder.build()
+
+ /**
+ * Create transaction commit request with the default request name.
+ *
+ * @return TransactionCommitActionBuilder.
+ */
+ def commit: TransactionCommitActionBuilder = TransactionCommitActionBuilder()
+
+ /**
+ * Create transaction commit request with the default request name.
+ *
+ * @return TransactionRollbackActionBuilder.
+ */
+ def rollback: TransactionRollbackActionBuilder = TransactionRollbackActionBuilder()
+}
+
+/**
+ * Transaction parameters.
+ *
+ * @param concurrency Concurrency.
+ * @param isolation Isolation.
+ * @param timeout Timeout in milliseconds.
+ * @param size Number of entries participating in transaction (may be approximate).
+ */
+case class TransactionParameters(
+ concurrency: Option[TransactionConcurrency] = None,
+ isolation: Option[TransactionIsolation] = None,
+ timeout: Option[Long] = None,
+ size: Option[Int] = None
+) {
+ /**
+ * Checks correctness of the parameters combination.
+ *
+ * @return true if parameters are valid.
+ */
+ def areValid: Boolean =
+ noneDefined(concurrency, isolation, timeout, size) ||
+ (allDefined(concurrency, isolation) && noneDefined(timeout, size)) ||
+ (allDefined(concurrency, isolation, timeout) && size.isEmpty) ||
+ allDefined(concurrency, isolation, timeout, size)
+
+ private def allDefined(options: Option[Any]*): Boolean =
+ options.forall(element => element.isDefined)
+
+ private def noneDefined(options: Option[Any]*): Boolean =
+ options.forall(element => element.isEmpty)
+
+ override def toString: String =
+ s"[concurrency=$concurrency, isolation=$isolation, timeout=$timeout, size=$size]"
+}
+
+/**
+ * Transaction builder.
+ *
+ * @param params Transaction parameters collected so far.
+ * @param transactionChain Chain of user actions to be executed within a transaction.
+ */
+case class TransactionBuilder(params: TransactionParameters, transactionChain: Seq[ChainBuilder]) extends NameGen {
+
+ /**
+ * Builds full chain of actions that make up a transaction.
+ *
+ * Full chain consists of:
+ * - transaction start action
+ * - chain of user-provided actions
+ * - close transaction action.
+ *
+ * @param requestName Request name.
+ * @return Full chain of actions.
+ */
+ def as(requestName: String): ChainBuilder =
+ group(_ => requestName.success)(
+ exec(TransactionStartActionBuilder(requestName, params))
+ .exec(transactionChain)
+ .exec(TransactionCloseActionBuilder(requestName))
+ )
+
+ /**
+ * Builds full chain of actions that make up a transaction with the default request name.
+ *
+ * @return Full chain of actions.
+ */
+ def build(): ChainBuilder = as(genName("tx"))
+}
+
+/**
+ * Transaction builder: step for transaction parameters.
+ *
+ * @param params Transaction parameters collected so far.
+ */
+case class TransactionBuilderParametersStep(params: TransactionParameters) {
+ /**
+ * Specify transaction timeout.
+ *
+ * @param timeout Timeout value in milliseconds.
+ * @return itself.
+ */
+ def timeout(timeout: Long): TransactionBuilderParametersStep =
+ TransactionBuilderParametersStep(params.copy(timeout = Some(timeout)))
+
+ /**
+ * Specify transaction size.
+ *
+ * @param size Number of entries participating in transaction (may be approximate).
+ * @return itself.
+ */
+ def size(size: Int): TransactionBuilderParametersStep =
+ TransactionBuilderParametersStep(params.copy(size = Some(size)))
+
+ /**
+ * Specify transaction concurrency.
+ *
+ * @param concurrency Concurrency.
+ * @return itself
+ */
+ def concurrency(concurrency: TransactionConcurrency): TransactionBuilderParametersStep =
+ TransactionBuilderParametersStep(params.copy(concurrency = Some(concurrency)))
+
+ /**
+ * Specify transaction isolation.
+ *
+ * @param isolation Isolation.
+ * @return itself
+ */
+ def isolation(isolation: TransactionIsolation): TransactionBuilderParametersStep =
+ TransactionBuilderParametersStep(params.copy(isolation = Some(isolation)))
+
+ /**
+ * Specify chain of actions that would be executed within a transaction.
+ *
+ * @param transactionChain Chain of user actions to be executed within a transaction.
+ * @return Full chain of actions.
+ */
+ def run(transactionChain: ChainBuilder*): TransactionBuilder = {
+ if (!params.areValid) {
+ throw new IgniteDslInvalidConfigurationException(
+ s"Wrong combination of transaction configuration parameters specified: $params"
+ )
+ }
+
+ if (containsAsyncActions(transactionChain)) {
+ throw new IgniteDslInvalidConfigurationException("Async Ignite API can not be used in transaction context")
+ }
+
+ TransactionBuilder(params, transactionChain)
+ }
+
+ private def containsAsyncActions(transactionChain: Seq[ChainBuilder]): Boolean =
+ transactionChain.exists(chainBuilder =>
+ chainBuilder.actionBuilders.exists(actionBuilder =>
+ actionBuilder match {
+ case builder: CacheActionCommonParameters => builder.withAsync
+ case _ => false
+ }
+ )
+ )
+}
+
+/**
+ * Transaction commit action builder.
+ *
+ * @param requestName Request name.
+ */
+case class TransactionCommitActionBuilder(requestName: String = "") extends ActionBuilder {
+ /**
+ * Specify request name for action.
+ *
+ * @param requestName Request name.
+ * @return itself.
+ */
+ def as(requestName: String): ActionBuilder = this.copy(requestName = requestName)
+
+ /**
+ * Builds an action.
+ *
+ * @param ctx The scenario context.
+ * @param next The action that will be chained with the Action build by this builder.
+ * @return The resulting action.
+ */
+ def build(ctx: ScenarioContext, next: Action): Action =
+ new TransactionCommitAction(requestName, next, ctx)
+}
+
+/**
+ * Transaction rollback action builder.
+ *
+ * @param requestName Request name.
+ */
+case class TransactionRollbackActionBuilder(requestName: String = "") extends ActionBuilder {
+ /**
+ * Specify request name for action.
+ *
+ * @param requestName Request name.
+ * @return itself.
+ */
+ def as(requestName: String): ActionBuilder = this.copy(requestName = requestName)
+
+ /**
+ * Builds an action.
+ *
+ * @param ctx The scenario context.
+ * @param next The action that will be chained with the Action build by this builder.
+ * @return The resulting action.
+ */
+ override def build(ctx: ScenarioContext, next: Action): TransactionRollbackAction =
+ new TransactionRollbackAction(requestName, next, ctx)
+}
+
+/**
+ * Transaction start action builder.
+ *
+ * @param requestName Request name.
+ * @param params Transaction parameters.
+ */
+case class TransactionStartActionBuilder(requestName: String, params: TransactionParameters) extends ActionBuilder {
+ /**
+ * Builds an action.
+ *
+ * @param ctx The scenario context.
+ * @param next The action that will be chained with the Action build by this builder.
+ * @return The resulting action.
+ */
+ override def build(ctx: ScenarioContext, next: Action): Action =
+ new TransactionStartAction(requestName, params, next, ctx)
+}
+
+/**
+ * Transaction close action builder.
+ *
+ * @param requestName Request name.
+ */
+case class TransactionCloseActionBuilder(requestName: String) extends ActionBuilder {
+ /**
+ * Builds an action.
+ *
+ * @param ctx The scenario context.
+ * @param next The action that will be chained with the Action build by this builder.
+ * @return The resulting action.
+ */
+ override def build(ctx: ScenarioContext, next: Action): Action =
+ new TransactionCloseAction(requestName, next, ctx)
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/check/IgniteCheckSupport.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/check/IgniteCheckSupport.scala
new file mode 100644
index 00000000..669f784c
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/check/IgniteCheckSupport.scala
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.check
+
+import io.gatling.commons.validation.FailureWrapper
+import io.gatling.commons.validation.SuccessWrapper
+import io.gatling.commons.validation.Validation
+import io.gatling.core.Predef.Session
+import io.gatling.core.check.Validator
+import io.gatling.core.session.Expression
+
+/**
+ * Check validation functions common for results obtained both from Ignite Key-Value and SQL APIs.
+ */
+trait IgniteCheckSupport {
+ /**
+ * Implicit conversion from lambda to Validator to support validation as:
+ * {{{
+ * ignite(
+ * sql(cache, "SELECT id, name FROM City WHERE id = ?").args("#{id}")
+ * check(
+ * resultSet.findAll.validate((rows: Seq[Row], s: Session) => row.head(1) == "Nsk")
+ * )
+ * )
+ * }}}
+ *
+ * @param predicate Predicate validating the result in context of the session.
+ * @tparam P Type of the prepared operation result.
+ * @return PredicateValidator.
+ */
+ implicit def predicateToValidator[P](predicate: (P, Session) => Boolean): Expression[Validator[P]] = s =>
+ new PredicateValidator[P](predicate, s).success
+
+ /**
+ * Validator calling the provided predicate function to validate the operation result.
+ *
+ * @param predicate Predicate lambda.
+ * @param session Session.
+ * @tparam P Type of the prepared operation result.
+ */
+ final class PredicateValidator[P](predicate: (P, Session) => Boolean, session: Session) extends Validator[P] {
+ /** Name of the validator. */
+ val name = "predicate"
+
+ /**
+ * Applies the predicate to validate the actual result.
+ *
+ * @param actual Actual result of operation.
+ * @param displayActualValue Display actual value.
+ * @return Validation instance.
+ */
+ def apply(actual: Option[P], displayActualValue: Boolean): Validation[Option[P]] =
+ if (predicate(actual.get, session)) {
+ actual.success
+ } else {
+ "check failed".failure
+ }
+ }
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/check/IgniteKeyValueCheckSupport.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/check/IgniteKeyValueCheckSupport.scala
new file mode 100644
index 00000000..e8198d4b
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/check/IgniteKeyValueCheckSupport.scala
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.check
+
+import io.gatling.core.check.Check
+import io.gatling.core.check.CheckBuilder
+import io.gatling.core.check.CheckMaterializer
+
+/**
+ * Base check validation functions for results obtained from the Ignite Key-Value API.
+ */
+trait IgniteKeyValueCheckSupport extends IgniteCheckSupport {
+ /**
+ * Type of the raw ignite key-value operation result.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the operation result.
+ */
+ type RawResult[K, V] = Map[K, V]
+
+ /**
+ * Alias for Ignite key-value operation result check type.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the cache value.
+ */
+ type IgniteCheck[K, V] = Check[RawResult[K, V]]
+
+ /**
+ * Implicit conversion from check builder to check itself.
+ *
+ * @param checkBuilder Check builder.
+ * @param materializer Materializer.
+ * @tparam T Check type.
+ * @tparam P Prepared result type.
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the operation result.
+ * @return IgniteCheck.
+ */
+ implicit def checkBuilder2IgniteCheck[T, P, K, V](checkBuilder: CheckBuilder[T, P])(implicit
+ materializer: CheckMaterializer[T, IgniteCheck[K, V], RawResult[K, V], P]
+ ): IgniteCheck[K, V] =
+ checkBuilder.build(materializer)
+
+ /**
+ * Implicit conversion to use the `exists` by default for Validate.
+ *
+ * @param validate Check validate.
+ * @param materializer Materializer.
+ * @tparam T Check type.
+ * @tparam P Type of the prepared result.
+ * @tparam X Type of the extracted result.
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the operation result.
+ * @return IgniteCheck.
+ */
+ implicit def validate2IgniteCheck[T, P, X, K, V](validate: CheckBuilder.Validate[T, P, X])(implicit
+ materializer: CheckMaterializer[T, IgniteCheck[K, V], RawResult[K, V], P]
+ ): IgniteCheck[K, V] =
+ validate.exists
+
+ /**
+ * Implicit conversion to use the `find(0).exists` by default for Find.
+ *
+ * @param find Find.
+ * @param materializer Materializer.
+ * @tparam T Check type.
+ * @tparam P Type of the prepared result.
+ * @tparam X Type of the extracted result.
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the operation result.
+ * @return IgniteCheck.
+ */
+ implicit def find2IgniteCheck[T, P, X, K, V](find: CheckBuilder.Find[T, P, X])(implicit
+ materializer: CheckMaterializer[T, IgniteCheck[K, V], RawResult[K, V], P]
+ ): IgniteCheck[K, V] =
+ find.find.exists
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/check/IgniteKeyValueEntriesCheckSupport.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/check/IgniteKeyValueEntriesCheckSupport.scala
new file mode 100644
index 00000000..38508478
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/check/IgniteKeyValueEntriesCheckSupport.scala
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.check
+
+import io.gatling.commons.validation.SuccessWrapper
+import io.gatling.core.check.CheckBuilder
+import io.gatling.core.check.CheckMaterializer
+import io.gatling.core.check.CountCriterionExtractor
+import io.gatling.core.check.Extractor
+import io.gatling.core.check.FindAllCriterionExtractor
+import io.gatling.core.check.FindCriterionExtractor
+import io.gatling.core.check.Preparer
+import io.gatling.core.session.Expression
+import io.gatling.core.session.ExpressionSuccessWrapper
+
+/**
+ * Support checks for the Ignite key-value operations results represented as a list of entries.
+ */
+trait IgniteKeyValueEntriesCheckSupport extends IgniteKeyValueCheckSupport {
+ /**
+ * Check of Key-Value operation result represented as a list of entries.
+ */
+ trait EntriesCheckType
+
+ /**
+ * Type of key-value operation entry.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the operation result.
+ * @param key Key.
+ * @param value Result.
+ */
+ case class Entry[K, V](key: K, value: V)
+
+ /**
+ * Type of the ignite key-value operation result prepared for check input.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the operation result.
+ */
+ type EntriesResult[K, V] = Seq[Entry[K, V]]
+
+ /**
+ * Materializer for Ignite key-value result check.
+ *
+ * Transform the raw response (which is a map) into sequence of entries which will be used as an input for check.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the operation result.
+ */
+ class EntriesCheckMaterializer[K, V]
+ extends CheckMaterializer[EntriesCheckType, IgniteCheck[K, V], RawResult[K, V], EntriesResult[K, V]](specializer = identity) {
+ /**
+ * @return Preparer which converts map to list of entries.
+ */
+ override protected def preparer: Preparer[RawResult[K, V], EntriesResult[K, V]] = _.filter { case (_, v) =>
+ v != null
+ }.toList.map(e => Entry(e._1, e._2)).success
+ }
+
+ /**
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the operation result.
+ * @return Implicit materiolizer for Ignite key-value result check.
+ */
+ implicit def entriesCheckMaterializer[K, V]: EntriesCheckMaterializer[K, V] = new EntriesCheckMaterializer[K, V]
+
+ /**
+ * Builder for the Ignite key-value operations result check exposed as an `entries` DSL function.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the operation result.
+ * @return Ignite check builder.
+ */
+ def entries[K, V]: CheckBuilder.MultipleFind.Default[EntriesCheckType, EntriesResult[K, V], Entry[K, V]] =
+ new CheckBuilder.MultipleFind.Default[EntriesCheckType, EntriesResult[K, V], Entry[K, V]](displayActualValue = true) {
+
+ override protected def findExtractor(occurrence: Int): Expression[Extractor[EntriesResult[K, V], Entry[K, V]]] =
+ new FindCriterionExtractor[EntriesResult[K, V], Int, Entry[K, V]](
+ "entry",
+ occurrence,
+ occurrence,
+ p => p.drop(occurrence).headOption.success
+ ).expressionSuccess
+
+ override protected def findAllExtractor: Expression[Extractor[EntriesResult[K, V], Seq[Entry[K, V]]]] =
+ new FindAllCriterionExtractor[EntriesResult[K, V], Int, Entry[K, V]](
+ "entry",
+ 0,
+ p => Some(p).success
+ ).expressionSuccess
+
+ override protected def countExtractor: Expression[Extractor[EntriesResult[K, V], Int]] =
+ new CountCriterionExtractor[EntriesResult[K, V], Int](
+ "entry",
+ 0,
+ p => Some(p.size).success
+ ).expressionSuccess
+ }
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/check/IgniteKeyValueMapResultCheckSupport.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/check/IgniteKeyValueMapResultCheckSupport.scala
new file mode 100644
index 00000000..d8503c94
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/check/IgniteKeyValueMapResultCheckSupport.scala
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.check
+
+import io.gatling.commons.validation.SuccessWrapper
+import io.gatling.commons.validation.Validation
+import io.gatling.core.check.CheckBuilder
+import io.gatling.core.check.CheckMaterializer
+import io.gatling.core.check.Extractor
+import io.gatling.core.check.Preparer
+import io.gatling.core.check.identityPreparer
+import io.gatling.core.session.Expression
+import io.gatling.core.session.ExpressionSuccessWrapper
+
+/**
+ * Support checks for the Ignite key-value operations results represented as a map.
+ */
+trait IgniteKeyValueMapResultCheckSupport extends IgniteKeyValueCheckSupport {
+ /**
+ * Check of Key-Value operation result represented as a map.
+ */
+ trait IgniteMapResultCheckType
+
+ /**
+ * Type of the Ignite key-value operation result.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the operation result.
+ */
+ type MapResult[K, V] = RawResult[K, V]
+
+ /**
+ * Materializer for Ignite key-value result check.
+ *
+ * Does nothing in fact - just return the raw operation result.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the operation result.
+ */
+ class MapResultCheckMaterializer[K, V]
+ extends CheckMaterializer[IgniteMapResultCheckType, IgniteCheck[K, V], MapResult[K, V], MapResult[K, V]](identity) {
+ /**
+ * Transform the raw response into something that will be used as check input.
+ * @return No-op preparer.
+ */
+ override protected def preparer: Preparer[MapResult[K, V], MapResult[K, V]] = identityPreparer
+ }
+
+ /**
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the operation result.
+ * @return Implicit materializer for Ignite key-value result check.
+ */
+ implicit def mapResultCheckMaterializer[K, V]: MapResultCheckMaterializer[K, V] = new MapResultCheckMaterializer[K, V]
+
+ /**
+ * mapResult extractor - does nothing - just return the prepared result as a whole.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the operation result.
+ * @return Extractor.
+ */
+ def mapResultExtractor[K, V]: Expression[Extractor[MapResult[K, V], MapResult[K, V]]] =
+ new Extractor[MapResult[K, V], MapResult[K, V]] {
+ override val name: String = "map"
+
+ override def apply(prepared: MapResult[K, V]): Validation[Option[MapResult[K, V]]] = Some(prepared).success
+
+ override val arity: String = "find"
+ }.expressionSuccess
+
+ /**
+ * Builder for the Ignite key-value operations result check exposed as a `mapResult` DSL function.
+ *
+ * @tparam K Type of the cache key.
+ * @tparam V Type of the operation result.
+ * @return Check builder.
+ */
+ def mapResult[K, V]: CheckBuilder.Find.Default[IgniteMapResultCheckType, MapResult[K, V], MapResult[K, V]] =
+ new CheckBuilder.Find.Default[IgniteMapResultCheckType, MapResult[K, V], MapResult[K, V]](
+ mapResultExtractor,
+ displayActualValue = true
+ )
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/check/IgniteSqlCheckSupport.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/check/IgniteSqlCheckSupport.scala
new file mode 100644
index 00000000..1a7bfd8a
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/check/IgniteSqlCheckSupport.scala
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.check
+
+import io.gatling.commons.validation.SuccessWrapper
+import io.gatling.core.check.Check
+import io.gatling.core.check.CheckBuilder
+import io.gatling.core.check.CheckMaterializer
+import io.gatling.core.check.CountCriterionExtractor
+import io.gatling.core.check.Extractor
+import io.gatling.core.check.FindAllCriterionExtractor
+import io.gatling.core.check.FindCriterionExtractor
+import io.gatling.core.check.Preparer
+import io.gatling.core.check.identityPreparer
+import io.gatling.core.session.Expression
+import io.gatling.core.session.ExpressionSuccessWrapper
+
+/**
+ * DSL for checks performed against the SQL query result set. Exposed as a `resultSet` check type.
+ *
+ * The result set is represented as a list of rows. Each row is a list of Any.
+ *
+ * Standard gatling extraction methods are supported: find, findAll, findRandom and count.
+ */
+trait IgniteSqlCheckSupport extends IgniteCheckSupport {
+ /**
+ * SQL query result check type.
+ */
+ trait SqlCheckType
+
+ /**
+ * Type of the SQL result set row.
+ */
+ type Row = List[Any]
+
+ /**
+ * Type of the raw Ignite SQL query operation result.
+ */
+ type RawSqlResult = List[Row]
+
+ /**
+ * Type of the prepared Ignite SQL query operation result.
+ */
+ type PreparedSqlResult = RawSqlResult
+
+ /**
+ * Check of Ignite SQL query operation result.
+ */
+ type SqlCheck = Check[RawSqlResult]
+
+ /**
+ * Implicit conversion from check builder to check itself.
+ *
+ * @param checkBuilder Check builder.
+ * @param materializer Materializer.
+ * @tparam T Check type.
+ * @tparam P Prepared result type.
+ * @return SqlCheck.
+ */
+ implicit def checkBuilder2SqlCheck[T, P](checkBuilder: CheckBuilder[T, P])(implicit
+ materializer: CheckMaterializer[T, SqlCheck, RawSqlResult, P]
+ ): SqlCheck =
+ checkBuilder.build(materializer)
+
+ /**
+ * Implicit conversion to use the `exists` by default for Validate.
+ *
+ * @param validate Validate instance.
+ * @param materializer Materializer.
+ * @tparam T Check type.
+ * @tparam P Type of the prepared result.
+ * @tparam X Type of the extracted result.
+ * @return SqlCheck.
+ */
+ implicit def validate2SqlCheck[T, P, X](validate: CheckBuilder.Validate[T, P, X])(implicit
+ materializer: CheckMaterializer[T, SqlCheck, RawSqlResult, P]
+ ): SqlCheck =
+ validate.exists
+
+ /**
+ * Implicit conversion to use the `find(0).exists` by default for Find.
+ *
+ * @param find Find.
+ * @param materializer Materializer.
+ * @tparam T Check type.
+ * @tparam P Type of the prepared result.
+ * @tparam X Type of the extracted result.
+ * @return SqlCheck.
+ */
+ implicit def find2SqlCheck[T, P, X](find: CheckBuilder.Find[T, P, X])(implicit
+ materializer: CheckMaterializer[T, SqlCheck, RawSqlResult, P]
+ ): SqlCheck =
+ find.find.exists
+
+ /**
+ * Implicit materializer for preparation of raw result as input for SQL check.
+ */
+ implicit val SqlCheckMaterializer: CheckMaterializer[SqlCheckType, SqlCheck, RawSqlResult, PreparedSqlResult] =
+ new CheckMaterializer[SqlCheckType, SqlCheck, RawSqlResult, PreparedSqlResult](identity) {
+ override protected def preparer: Preparer[RawSqlResult, PreparedSqlResult] = identityPreparer
+ }
+
+ /**
+ * Builder for the SQL query result check exposed as a `resultSet` DSL function.
+ *
+ * @return SQL check builder
+ */
+ def resultSet: CheckBuilder.MultipleFind.Default[SqlCheckType, PreparedSqlResult, Row] =
+ new CheckBuilder.MultipleFind.Default[SqlCheckType, PreparedSqlResult, Row](displayActualValue = true) {
+ override protected def findExtractor(occurrence: Int): Expression[Extractor[PreparedSqlResult, Row]] =
+ new FindCriterionExtractor[PreparedSqlResult, Int, Row](
+ "row",
+ occurrence,
+ occurrence,
+ p => p.drop(occurrence).headOption.success
+ ).expressionSuccess
+
+ override protected def findAllExtractor: Expression[Extractor[PreparedSqlResult, Seq[Row]]] =
+ new FindAllCriterionExtractor[PreparedSqlResult, Int, Row](
+ "row",
+ 0,
+ p => Some(p).success
+ ).expressionSuccess
+
+ override protected def countExtractor: Expression[Extractor[PreparedSqlResult, Int]] =
+ new CountCriterionExtractor[PreparedSqlResult, Int](
+ "row",
+ 0,
+ p => Some(p.size).success
+ ).expressionSuccess
+ }
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/protocol/IgniteClientPool.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/protocol/IgniteClientPool.scala
new file mode 100644
index 00000000..21179410
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/protocol/IgniteClientPool.scala
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.protocol
+
+import java.util.concurrent.ConcurrentLinkedQueue
+
+import io.netty.util.internal.ThreadLocalRandom
+import org.apache.ignite.Ignition
+import org.apache.ignite.client.IgniteClient
+import org.apache.ignite.configuration.ClientConfiguration
+
+/**
+ * Pool of the Ignite (thin) client instances.
+ */
+trait IgniteClientPool {
+ /**
+ * @return Initialized IgniteClient instance.
+ */
+ def apply(): IgniteClient
+
+ /**
+ * Close any IgniteClient instances created.
+ */
+ def close(): Unit
+}
+
+/**
+ * Pool creating single IgniteClient instance per calling thread.
+ *
+ * @param cfg Thin client configuration.
+ */
+class IgniteClientPerThreadPool(cfg: ClientConfiguration) extends IgniteClientPool {
+ private val clients = new ConcurrentLinkedQueue[IgniteClient]()
+
+ private val client = ThreadLocal.withInitial[IgniteClient] { () =>
+ val client = Ignition.startClient(cfg)
+
+ clients.add(client)
+
+ client
+ }
+
+ override def apply(): IgniteClient = client.get()
+
+ override def close(): Unit = {
+ clients.forEach(c => c.close())
+
+ clients.clear()
+ }
+}
+
+/**
+ * Fixed size IgniteClients pool.
+ *
+ * @param cfg Thin client configuration.
+ * @param size Size of the pool.
+ */
+class IgniteClientFixedSizePool(cfg: ClientConfiguration, size: Int) extends IgniteClientPool {
+ private val clients = (0 until size).map(_ => Ignition.startClient(cfg)).toArray
+
+ override def apply(): IgniteClient = if (size == 1) {
+ clients(0)
+ } else {
+ clients(ThreadLocalRandom.current().nextInt(size))
+ }
+
+ override def close(): Unit =
+ clients.foreach(c => c.close())
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/protocol/IgniteComponents.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/protocol/IgniteComponents.scala
new file mode 100644
index 00000000..588419d9
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/protocol/IgniteComponents.scala
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.protocol
+
+import com.typesafe.scalalogging.StrictLogging
+import io.gatling.commons.util.Clock
+import io.gatling.core.CoreComponents
+import io.gatling.core.protocol.ProtocolComponents
+import io.gatling.core.session.Session
+import org.apache.ignite.gatling.api.IgniteApi
+import org.apache.ignite.gatling.protocol.IgniteProtocol.IgniteApiSessionKey
+
+/**
+ * Ignite gatling protocol components holder.
+ *
+ * @param coreComponents Core Gatling components.
+ * @param igniteProtocol Ignite protocol instance.
+ * @param igniteApi Shared default Ignite API instance.
+ * @param clock Clock to be used to measure action duration.
+ */
+case class IgniteComponents(
+ coreComponents: CoreComponents,
+ igniteProtocol: IgniteProtocol,
+ igniteApi: Option[IgniteApi] = None,
+ clock: Option[Clock] = None
+) extends ProtocolComponents
+ with StrictLogging {
+
+ /**
+ * Return lambda to init client session before injection into scenario.
+ *
+ * Lambda puts default shared Ignite API instance (if it exists) into the session object.
+ *
+ * @return Lambda to be called to init session.
+ */
+ override def onStart: Session => Session = session =>
+ igniteApi
+ .map(api => session.set(IgniteApiSessionKey, api))
+ .getOrElse(session)
+
+ /**
+ * Return lambda to clean-up the client session after scenario finish.
+ *
+ * Lambda closes Ignite API instance if it is still in the session (unless it's a default shared instance).
+ *
+ * @return Lambda to be called to clean-up session.
+ */
+ override def onExit: Session => Unit = session =>
+ if (igniteApi.isEmpty) {
+ session(IgniteApiSessionKey)
+ .asOption[IgniteApi]
+ .foreach(_.close()(_ => (), _ => ()))
+ }
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/protocol/IgniteProtocol.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/protocol/IgniteProtocol.scala
new file mode 100644
index 00000000..3edcfc37
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/protocol/IgniteProtocol.scala
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.protocol
+
+import io.gatling.commons.util.Clock
+import io.gatling.core.CoreComponents
+import io.gatling.core.config.GatlingConfiguration
+import io.gatling.core.protocol.Protocol
+import io.gatling.core.protocol.ProtocolKey
+import org.apache.ignite.Ignite
+import org.apache.ignite.client.IgniteClient
+import org.apache.ignite.configuration.ClientConfiguration
+import org.apache.ignite.configuration.IgniteConfiguration
+import org.apache.ignite.gatling.api.IgniteApi
+
+/**
+ * Ignite protocol globals.
+ */
+object IgniteProtocol {
+ /** Session key the IgniteApi instance is stored under. */
+ val IgniteApiSessionKey = "__igniteApi__"
+
+ /**
+ * Session key the TransactionApi instance is stored under.
+ *
+ * Presence of the TransactionApi instance in session means that the Ignite transaction is active now.
+ */
+ val TransactionApiSessionKey = "__transactionApi__"
+
+ /**
+ * Session key for explicit locks flag.
+ *
+ * Presence of flag means that explicit locks were created in scenario.
+ */
+ val ExplicitLockWasUsedSessionKey = "__explicitLockWasUsed__"
+
+ /**
+ * Initialise components of the Ignite protocol on simulation start.
+ */
+ val IgniteProtocolKey: ProtocolKey[IgniteProtocol, IgniteComponents] = new ProtocolKey[IgniteProtocol, IgniteComponents] {
+ override def protocolClass: Class[Protocol] =
+ classOf[IgniteProtocol].asInstanceOf[Class[Protocol]]
+
+ override def defaultProtocolValue(configuration: GatlingConfiguration): IgniteProtocol =
+ IgniteProtocolBuilderBase.igniteCfg(new IgniteConfiguration()).build
+
+ /**
+ * Return lambda to init the Ignite protocol components.
+ *
+ * Note, lambda would start new Ignite API instance if none was passed as a protocol configuration parameter
+ * (unless the `explicitClientStart` protocol parameter was used).
+ *
+ * If IGNITE_GATLING_USE_NANO_CLOCK system property is set substitutes the default Gatling clocks (which
+ * measures time in millis) with clocks measuring time in nanoseconds. This clocks will be used to measure
+ * duration of Ignite actions. This gives more precision for sub-millis requests.
+ *
+ * Note however that using nano clocks means the following limitations:
+ * - Native Gatling report generation doesn't work. So custom reporting should be implemented which would
+ * directly parse the Gatling generated simulation.log files.
+ * - Action groups duration measuring doesn't work. So testing scenarios should not contains any groups.
+ *
+ * @param coreComponents Gatling core components.
+ * @return Lambda creating Ignite components from the Ignite protocol parameters provided.
+ */
+ override def newComponents(coreComponents: CoreComponents): IgniteProtocol => IgniteComponents =
+ igniteProtocol => {
+ val components = IgniteComponents(
+ coreComponents,
+ igniteProtocol,
+ IgniteApi(igniteProtocol),
+ Option(System.getProperty("IGNITE_GATLING_USE_NANO_CLOCK")).map(_ => new NanoClock())
+ )
+
+ igniteProtocol.cfg match {
+ case IgniteClientConfigurationCfg(_) if !igniteProtocol.explicitClientStart => igniteProtocol.api = components.igniteApi
+
+ case IgniteConfigurationCfg(_) if !igniteProtocol.explicitClientStart => igniteProtocol.api = components.igniteApi
+
+ case _ =>
+ }
+
+ components
+ }
+ }
+
+ /**
+ * Clock implementation measuring time in nanoseconds.
+ *
+ * NOTE, it returns nanoseconds from nowMillis() method !!!
+ */
+ private class NanoClock extends Clock {
+ override def nowSeconds: Long = nowMillis / 1000000
+
+ private val currentTimeMillisReference = System.currentTimeMillis
+ private val nanoTimeReference = System.nanoTime
+
+ override def nowMillis: Long = (System.nanoTime - nanoTimeReference) + currentTimeMillisReference * 1000000
+ }
+}
+
+/**
+ * Ignite protocol parameters.
+ *
+ * @param cfg Ignite API configuration.
+ * @param explicitClientStart If true the default shared instance of Ignite API will not be started before the
+ * simulation start and it will not be automatically inserted into the client session
+ * upon injection into the scenario. To start Ignite API instance scenario should contain
+ * explicit `startIgniteApi` and `closeIgniteApi` actions.
+ * @param api Ignite API instance if started during the simulation. Stored here to be able to close it after
+ * simulation finish.
+ */
+case class IgniteProtocol(cfg: IgniteCfg, explicitClientStart: Boolean = false, var api: Option[IgniteApi] = None) extends Protocol {
+ /**
+ * Close all opened clients.
+ */
+ def close(): Unit =
+ cfg match {
+ case cfg: IgniteClientPoolCfg => cfg.pool.close()
+ case _: IgniteConfigurationCfg => api.foreach(api => api.close()(_ => (), _ => ()))
+ case _: IgniteClientConfigurationCfg => api.foreach(api => api.close()(_ => (), _ => ()))
+ case _ =>
+ }
+}
+
+/**
+ * Abstract Ignite API configuration.
+ */
+sealed trait IgniteCfg
+
+/**
+ * Ignite API configuration containing the pre-started Ignite (thin) Client instance.
+ *
+ * @param client Instance of Ignite (thin) Client.
+ */
+case class IgniteClientCfg(client: IgniteClient) extends IgniteCfg
+
+/**
+ * Ignite API configuration containing the pre-started Ignite (thick) node instance.
+ *
+ * @param ignite Instance of the Ignite grid.
+ */
+case class IgniteNodeCfg(ignite: Ignite) extends IgniteCfg
+
+/**
+ * Ignite API configuration containing the Ignite (thin) Client configuration instance.
+ *
+ * @param cfg Ignite (thin) client configuration.
+ */
+case class IgniteClientConfigurationCfg(cfg: ClientConfiguration) extends IgniteCfg
+
+/**
+ * Ignite API configuration containing the Ignite node (thick) configuration instance.
+ *
+ * @param cfg Ignite (thick) node configuration.
+ */
+case class IgniteConfigurationCfg(cfg: IgniteConfiguration) extends IgniteCfg
+
+/**
+ * Ignite API configuration containing the Ignite (thin) Client instances pool.
+ *
+ * @param pool Ignite (thin) client instances pool.
+ */
+case class IgniteClientPoolCfg(pool: IgniteClientPool) extends IgniteCfg
diff --git a/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/protocol/IgniteProtocolSupport.scala b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/protocol/IgniteProtocolSupport.scala
new file mode 100644
index 00000000..312a156e
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/main/scala/org/apache/ignite/gatling/protocol/IgniteProtocolSupport.scala
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling.protocol
+
+import java.net.URL
+
+import scala.jdk.CollectionConverters._
+
+import org.apache.ignite.Ignite
+import org.apache.ignite.client.IgniteClient
+import org.apache.ignite.configuration.ClientConfiguration
+import org.apache.ignite.configuration.IgniteConfiguration
+import org.apache.ignite.internal.IgniteComponentType.SPRING
+import org.apache.ignite.internal.IgnitionEx
+import org.apache.ignite.internal.util.IgniteUtils
+import org.apache.ignite.internal.util.spring.IgniteSpringHelper
+
+trait IgniteProtocolSupport {
+ /** Ignite protocol builder starting point. */
+ val igniteProtocol: IgniteProtocolBuilderBase.type = IgniteProtocolBuilderBase
+
+ /**
+ * Implicit to build protocol from the IgniteProtocolBuilder.
+ * @param builder Protocol builder.
+ * @return Protocol.
+ */
+ implicit def builder2igniteProtocol(builder: IgniteProtocolBuilder): IgniteProtocol = builder.build
+
+ /**
+ * Implicit to build protocol from the IgniteProtocolBuilderExplicitStartStep.
+ * @param builder Protocol builder.
+ * @return Protocol.
+ */
+ implicit def builderExplicitStartStep2igniteProtocol(builder: IgniteProtocolBuilderExplicitStartStep): IgniteProtocol = builder.build
+}
+
+/**
+ * Base Ignite protocol builder.
+ */
+case object IgniteProtocolBuilderBase {
+ /**
+ * Specify Ignite API as pre-started Ignite (thin) Client instance.
+ *
+ * @param igniteClient IgniteClient instance.
+ * @return Build step for additional protocol parameters.
+ */
+ def client(igniteClient: IgniteClient): IgniteProtocolBuilder =
+ IgniteProtocolBuilder(IgniteClientCfg(igniteClient), explicitClientStart = false)
+
+ /**
+ * Specify Ignite API as Ignite (thin) client configuration.
+ *
+ * @param cfg ClientConfiguration instance.
+ * @return Build step for additional protocol parameters.
+ */
+ def clientCfg(cfg: ClientConfiguration): IgniteProtocolBuilderExplicitStartStep =
+ IgniteProtocolBuilderExplicitStartStep(IgniteClientConfigurationCfg(cfg))
+
+ /**
+ * Specify Ignite API as Ignite (thin) client Spring XML configuration file.
+ *
+ * @param cfgUrl URL of Spring XML configuration file containing the ClientConfiguration bean.
+ * @return Build step for additional protocol parameters.
+ */
+ def clientCfgPath(cfgUrl: URL): IgniteProtocolBuilderExplicitStartStep = {
+ val spring: IgniteSpringHelper = SPRING.create(false)
+
+ val clientCfg = spring.loadConfigurations(cfgUrl, classOf[ClientConfiguration])
+ .get1().asScala.head
+
+ IgniteProtocolBuilderExplicitStartStep(IgniteClientConfigurationCfg(clientCfg))
+ }
+
+ /**
+ * Specify Ignite API as Ignite (thin) client Spring XML configuration file.
+ *
+ * @param cfgPath Path to Spring XML configuration file containing the ClientConfiguration bean.
+ * @return Build step for additional protocol parameters.
+ */
+ def clientCfgPath(cfgPath: String): IgniteProtocolBuilderExplicitStartStep =
+ clientCfgPath(IgniteUtils.resolveSpringUrl(cfgPath))
+
+ /**
+ * Specify Ignite API as Ignite (thin) Client instances pool.
+ *
+ * @param pool Pool of IgniteClient instances.
+ * @return Build step for additional protocol parameters.
+ */
+ def clientPool(pool: IgniteClientPool): IgniteProtocolBuilder =
+ IgniteProtocolBuilder(IgniteClientPoolCfg(pool), explicitClientStart = false)
+
+ /**
+ * Specify Ignite API as pre-started Ignite (thick) node instance.
+ *
+ * @param ignite Ignite instance.
+ * @return Build step for additional protocol parameters.
+ */
+ def ignite(ignite: Ignite): IgniteProtocolBuilder =
+ IgniteProtocolBuilder(IgniteNodeCfg(ignite), explicitClientStart = false)
+
+ /**
+ * Specify Ignite API as Ignite (thick) node configuration.
+ *
+ * @param cfg IgniteConfiguration instance.
+ * @return Build step for additional protocol parameters.
+ */
+ def igniteCfg(cfg: IgniteConfiguration): IgniteProtocolBuilder =
+ IgniteProtocolBuilder(IgniteConfigurationCfg(cfg), explicitClientStart = false)
+
+ /**
+ * Specify Ignite API as Ignite (thick) node Spring XML configuration file.
+ *
+ * @param cfgUrl URL of Spring XML configuration file containing the IgniteConfiguration bean.
+ * @return Build step for additional protocol parameters.
+ */
+ def igniteCfgPath(cfgUrl: URL): IgniteProtocolBuilder =
+ IgniteProtocolBuilder(IgniteConfigurationCfg(IgnitionEx.loadConfiguration(cfgUrl).get1()), explicitClientStart = false)
+
+ /**
+ * Specify Ignite API as Ignite (thick) node Spring XML configuration file.
+ *
+ * @param cfgPath Path to Spring XML configuration file containing the IgniteConfiguration bean.
+ * @return Build step for additional protocol parameters.
+ */
+ def igniteCfgPath(cfgPath: String): IgniteProtocolBuilder =
+ igniteCfgPath(IgniteUtils.resolveSpringUrl(cfgPath))
+}
+
+/**
+ * Builder step for additional protocol parameters.
+ *
+ * @param cfg Ignite API configuration.
+ */
+case class IgniteProtocolBuilderExplicitStartStep(cfg: IgniteCfg) {
+ /**
+ * Specify the `withExplicitClientStart` flag.
+ * @return Protocol builder further step.
+ */
+ def withExplicitClientStart: IgniteProtocolBuilder = IgniteProtocolBuilder(cfg, explicitClientStart = true)
+
+ /**
+ * Builds Ignite protocol instance.
+ * @return Protocol builder further step.
+ */
+ def build: IgniteProtocol = IgniteProtocolBuilder(cfg, explicitClientStart = false).build
+}
+
+/**
+ * Ignite protocol builder step for other parameters.
+ *
+ * @param cfg Ignite API configuration.
+ * @param explicitClientStart Explicit client start flag.
+ */
+case class IgniteProtocolBuilder(cfg: IgniteCfg, explicitClientStart: Boolean) {
+ /**
+ * Builds Ignite protocol instance.
+ * @return Protocol builder further step.
+ */
+ def build: IgniteProtocol = IgniteProtocol(cfg, explicitClientStart)
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/test/resources/ddl.sql b/modules/gatling-ext/gatling-plugin/src/test/resources/ddl.sql
new file mode 100644
index 00000000..b34b6c67
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/test/resources/ddl.sql
@@ -0,0 +1,3 @@
+CREATE TABLE City (id int primary key, name varchar, region varchar)
+ALTER TABLE IF EXISTS City ADD IF NOT EXISTS sharding_id BIGINT
+ALTER TABLE IF EXISTS City ADD IF NOT EXISTS population INT
diff --git a/modules/gatling-ext/gatling-plugin/src/test/resources/ignite-config.xml b/modules/gatling-ext/gatling-plugin/src/test/resources/ignite-config.xml
new file mode 100644
index 00000000..a7a70030
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/test/resources/ignite-config.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
+ <bean class="org.apache.ignite.configuration.IgniteConfiguration">
+ <property name="clientMode" value="true"/>
+ <property name="igniteInstanceName" value="client"/>
+ <property name="localHost" value="127.0.0.1"/>
+ <property name="peerClassLoadingEnabled" value="true"/>
+ <property name="discoverySpi">
+ <bean class="org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi">
+ <property name="joinTimeout" value="10000"/>
+ <property name="ipFinder">
+ <bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder">
+ <property name="addresses">
+ <list>
+ <value>127.0.0.1:47500..47600</value>
+ </list>
+ </property>
+ </bean>
+ </property>
+ </bean>
+ </property>
+ </bean>
+</beans>
diff --git a/modules/gatling-ext/gatling-plugin/src/test/resources/ignite-thin-config.xml b/modules/gatling-ext/gatling-plugin/src/test/resources/ignite-thin-config.xml
new file mode 100644
index 00000000..60be579f
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/test/resources/ignite-thin-config.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
+ <bean class="org.apache.ignite.configuration.ClientConfiguration">
+ <property name="addresses">
+ <list>
+ <value>localhost:10800</value>
+ </list>
+ </property>
+ </bean>
+</beans>
diff --git a/modules/gatling-ext/gatling-plugin/src/test/scala/org/apache/ignite/gatling/BinaryTest.scala b/modules/gatling-ext/gatling-plugin/src/test/scala/org/apache/ignite/gatling/BinaryTest.scala
new file mode 100644
index 00000000..b5f88dd8
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/test/scala/org/apache/ignite/gatling/BinaryTest.scala
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling
+
+import com.typesafe.scalalogging.StrictLogging
+import io.gatling.commons.validation.SuccessWrapper
+import io.gatling.core.Predef._
+import org.apache.ignite.binary.BinaryObject
+import org.apache.ignite.gatling.Predef._
+import org.apache.ignite.gatling.Predef.group
+import org.apache.ignite.gatling.api.node.IgniteNodeApi
+import org.apache.ignite.gatling.api.thin.IgniteThinApi
+import org.apache.ignite.gatling.utils.AbstractGatlingTest
+import org.apache.ignite.gatling.utils.IgniteClientApi.NodeApi
+import org.apache.ignite.gatling.utils.IgniteClientApi.ThinClient
+import org.apache.ignite.gatling.utils.IgniteSupport
+import org.junit.Test
+
+/**
+ * Tests operations with binary objects and low-level access to underlining Ignite API
+ * for functionality not exposed via the DSL.
+ */
+class BinaryTest extends AbstractGatlingTest {
+ /** Class name of simulation */
+ val simulation: String = classOf[BinarySimulation].getName
+
+ /** Runs simulation with thin client. */
+ @Test
+ def thinClient(): Unit = runWith(ThinClient)(simulation)
+
+ /** Runs simulation with thick client. */
+ @Test
+ def thickClient(): Unit = runWith(NodeApi)(simulation)
+}
+
+/**
+ */
+class BinarySimulation extends Simulation with IgniteSupport with StrictLogging {
+ private val bootstrapCache = "bootstrap-cache"
+
+ private val scn = scenario("Binary")
+ .feed(feeder)
+ .ignite(
+ getOrCreateCache(bootstrapCache),
+ sql(
+ bootstrapCache,
+ "CREATE TABLE City (id int primary key, name varchar, region varchar) WITH \"cache_name=s.city,value_type=s.city\""
+ ),
+ group("run outside of transaction")(
+ operationsWithTable("s.city")
+ ),
+ sql(
+ bootstrapCache,
+ "CREATE TABLE City2 (id int primary key, name varchar, region varchar) WITH \"atomicity=transactional,cache_name=s.city2,value_type=s.city2\""
+ ),
+ tx run (
+ operationsWithTable("s.city2")
+ ) as "run in transaction"
+ )
+
+ private def operationsWithTable(cache: String) = ignite(
+ // Put binary object
+ put[Int, BinaryObject](
+ cache,
+ "#{key}",
+ session =>
+ session
+ .binaryBuilder()(typeName = cache)
+ .setField("id", session("key").as[Int])
+ .setField("name", "Nsk")
+ .setField("region", "Region")
+ .build()
+ .success
+ ).keepBinary,
+
+ // sql select with the affinity awareness
+ sql(cache, "SELECT * FROM City WHERE id = ?")
+ .args("#{key}")
+ .partitions { session =>
+ (session.igniteApi match {
+ case IgniteNodeApi(ignite) => List(ignite.affinity(cache).partition(session("key").as[Int]))
+
+ case IgniteThinApi(_) => List.empty
+ }).success
+ }
+ .check(
+ resultSet.count.is(1),
+ resultSet.transform(row => row(1)).is("Nsk")
+ )
+ )
+
+ setUp(scn.inject(atOnceUsers(1)))
+ .protocols(protocol)
+ .assertions(
+ global.failedRequests.count.is(0)
+ )
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/test/scala/org/apache/ignite/gatling/CreateCacheTest.scala b/modules/gatling-ext/gatling-plugin/src/test/scala/org/apache/ignite/gatling/CreateCacheTest.scala
new file mode 100644
index 00000000..6e4a8128
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/test/scala/org/apache/ignite/gatling/CreateCacheTest.scala
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling
+
+import com.typesafe.scalalogging.StrictLogging
+import io.gatling.core.Predef._
+import org.apache.ignite.client.ClientCacheConfiguration
+import org.apache.ignite.configuration.CacheConfiguration
+import org.apache.ignite.gatling.Predef._
+import org.apache.ignite.gatling.utils.AbstractGatlingTest
+import org.apache.ignite.gatling.utils.IgniteClientApi.NodeApi
+import org.apache.ignite.gatling.utils.IgniteClientApi.ThinClient
+import org.apache.ignite.gatling.utils.IgniteSupport
+import org.junit.Test
+
+/**
+ * Tests various create cache operations.
+ */
+class CreateCacheTest extends AbstractGatlingTest {
+ /** Create cache via thin client and simple DSL parameters. */
+ @Test
+ def thinClientDSL(): Unit = runWith(ThinClient)(classOf[CreateCacheDSLSimulation].getName)
+
+ /** Create cache via thick client and simple DSL parameters. */
+ @Test
+ def thickClientDSL(): Unit = runWith(NodeApi)(classOf[CreateCacheDSLSimulation].getName)
+
+ /** Create cache via thin client and full configuration instance. */
+ @Test
+ def thinClientConfig(): Unit = runWith(ThinClient)(classOf[CreateCacheThinConfigSimulation].getName)
+
+ /** Create cache via thick client and full configuration instance. */
+ @Test
+ def thickClientConfig(): Unit = runWith(NodeApi)(classOf[CreateCacheThickConfigSimulation].getName)
+}
+
+/**
+ * Create cache with simple parameters specified via DSL.
+ */
+class CreateCacheDSLSimulation extends Simulation with IgniteSupport with StrictLogging {
+ private val cache = "cache"
+
+ private val scn = scenario("Basic")
+ .feed(feeder)
+ .ignite(
+ getOrCreateCache(s"$cache-1") as "1",
+ getOrCreateCache(s"$cache-2").backups(1) as "2",
+ getOrCreateCache(s"$cache-3").atomicity(TRANSACTIONAL) as "3",
+ getOrCreateCache(s"$cache-4").mode(PARTITIONED).backups(1) as "4",
+ getOrCreateCache(s"$cache-5") backups 1 atomicity ATOMIC mode REPLICATED as "5"
+ )
+
+ setUp(scn.inject(atOnceUsers(1)))
+ .protocols(protocol)
+ .assertions(
+ global.failedRequests.count.is(0)
+ )
+}
+
+/**
+ * Create cache using full ClientCacheConfiguration instance.
+ */
+class CreateCacheThinConfigSimulation extends Simulation with IgniteSupport with StrictLogging {
+ private val cache = "cache-thin"
+
+ private val scn = scenario("Basic")
+ .feed(feeder)
+ .ignite(
+ getOrCreateCache(cache).cfg(new ClientCacheConfiguration())
+ )
+
+ setUp(scn.inject(atOnceUsers(1)))
+ .protocols(protocol)
+ .assertions(
+ global.failedRequests.count.is(0)
+ )
+}
+
+/**
+ * Create cache using full CacheConfiguration instance.
+ */
+class CreateCacheThickConfigSimulation extends Simulation with IgniteSupport with StrictLogging {
+ private val cache = "cache-thick"
+
+ private val scn = scenario("Basic")
+ .feed(feeder)
+ .ignite(
+ getOrCreateCache(cache).cfg(new CacheConfiguration[Int, Int]()) as "create"
+ )
+
+ setUp(scn.inject(atOnceUsers(1)))
+ .protocols(protocol)
+ .assertions(
+ global.failedRequests.count.is(0)
+ )
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/test/scala/org/apache/ignite/gatling/InvokeAllTest.scala b/modules/gatling-ext/gatling-plugin/src/test/scala/org/apache/ignite/gatling/InvokeAllTest.scala
new file mode 100644
index 00000000..753f8889
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/test/scala/org/apache/ignite/gatling/InvokeAllTest.scala
@@ -0,0 +1,184 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling
+
+import javax.cache.processor.MutableEntry
+
+import scala.collection.SortedMap
+import scala.collection.SortedSet
+import scala.language.postfixOps
+
+import com.typesafe.scalalogging.StrictLogging
+import io.gatling.core.Predef._
+import io.gatling.core.session.ExpressionSuccessWrapper
+import org.apache.ignite.cache.CacheEntryProcessor
+import org.apache.ignite.gatling.InvokeAllTest.getBatch
+import org.apache.ignite.gatling.InvokeAllTest.testCache1
+import org.apache.ignite.gatling.InvokeAllTest.testCache2
+import org.apache.ignite.gatling.Predef._
+import org.apache.ignite.gatling.Predef.group
+import org.apache.ignite.gatling.utils.AbstractGatlingTest
+import org.apache.ignite.gatling.utils.IgniteClientApi.NodeApi
+import org.apache.ignite.gatling.utils.IgniteSupport
+import org.junit.Test
+
+/**
+ * Tests invokeAll.
+ */
+class InvokeAllTest extends AbstractGatlingTest {
+ @Test
+ /** Tests simulation with the single entry processor passed to invokeAll. */
+ def testSingleProcessor(): Unit = runWith(NodeApi)(classOf[SingleProcessorSimulation].getName)
+
+ @Test
+ /** Tests simulation with multiple entry processors passed to invokeAll. */
+ def multipleProcessors(): Unit = runWith(NodeApi)(classOf[MultipleProcessorsSimulation].getName)
+}
+
+object InvokeAllTest {
+ /** First test cache */
+ val testCache1 = "TEST-CACHE-1"
+
+ /** Second test cache */
+ val testCache2 = "TEST-CACHE-2"
+
+ /** Helper to extract the fed batch from session */
+ val getBatch = "#{batch}"
+}
+
+/**
+ * invokeAll with single entry processor simulation.
+ */
+class SingleProcessorSimulation extends Simulation with IgniteSupport with StrictLogging {
+ private val scn = scenario("invokeAll")
+ .feed(new BatchFeeder())
+ .ignite(
+ getOrCreateCache(testCache1) backups 1 atomicity ATOMIC mode PARTITIONED as "create",
+ group("run outside of transaction")(
+ asyncOperationsWithCache(testCache1)
+ ),
+ getOrCreateCache(testCache2) atomicity TRANSACTIONAL mode REPLICATED,
+ tx run (
+ syncOperationsWithCache(testCache2)
+ ) as "run in transaction"
+ )
+
+ private def syncOperationsWithCache(cache: String) = ignite(
+ putAll[Int, Int](cache, getBatch),
+ invokeAll[Int, Int, Unit](cache, SortedSet(1, 3, 5).expressionSuccess) { e: MutableEntry[Int, Int] =>
+ e.setValue(-e.getValue)
+ },
+ invokeAll[Int, Int, Unit](cache, SortedSet(1, 5, 3).expressionSuccess).args(3.expressionSuccess) {
+ (e: MutableEntry[Int, Int], args: Seq[Any]) =>
+ e.setValue(e.getValue * args.head.asInstanceOf[Integer])
+ },
+ getAll[Int, Int](cache, SortedSet(1, 3, 5))
+ .check(
+ entries[Int, Int].findAll.validate((entries: Seq[Entry[Int, Int]], _: Session) =>
+ entries.forall(e => e.value == -3 * (e.key + 1))
+ )
+ ) as "getAll"
+ )
+
+ private def asyncOperationsWithCache(cache: String) = ignite(
+ putAll[Int, Int](cache, getBatch) async,
+ invokeAll[Int, Int, Unit](cache, SortedSet(1, 3, 5).expressionSuccess) { e: MutableEntry[Int, Int] =>
+ e.setValue(-e.getValue)
+ } async,
+ invokeAll[Int, Int, Unit](cache, SortedSet(1, 5, 3).expressionSuccess).args(3.expressionSuccess) {
+ (e: MutableEntry[Int, Int], args: Seq[Any]) =>
+ e.setValue(e.getValue * args.head.asInstanceOf[Integer])
+ } async,
+ getAll[Int, Int](cache, SortedSet(1, 3, 5))
+ .check(
+ entries[Int, Int].findAll.validate((entries: Seq[Entry[Int, Int]], _: Session) =>
+ entries.forall(e => e.value == -3 * (e.key + 1))
+ )
+ )
+ .async as "getAll"
+ )
+
+ setUp(scn.inject(atOnceUsers(1)))
+ .protocols(protocol)
+ .assertions(
+ global.failedRequests.count.is(0)
+ )
+}
+
+/** Entry processor with arguments. */
+private class EntryProcessor extends CacheEntryProcessor[Int, Int, Unit]() {
+ override def process(e: MutableEntry[Int, Int], args: Object*): Unit =
+ e.setValue(e.getValue * args.toList.head.asInstanceOf[Integer])
+}
+
+/**
+ * invokeAll with multiple entry processors simulation.
+ */
+class MultipleProcessorsSimulation extends Simulation with IgniteSupport with StrictLogging {
+ private val scn = scenario("invokeAll")
+ .feed(new BatchFeeder())
+ .ignite(
+ getOrCreateCache(testCache1) backups 1 atomicity ATOMIC mode PARTITIONED as "create",
+ group("run outside of transaction")(
+ asyncOperationsWithCache(testCache1)
+ ),
+ getOrCreateCache(testCache2) atomicity TRANSACTIONAL mode REPLICATED,
+ tx run (
+ syncOperationsWithCache(testCache2)
+ ) as "run in transaction"
+ )
+
+ private def syncOperationsWithCache(cache: String) = ignite(
+ putAll[Int, Int](cache, getBatch),
+ invokeAll[Int, Int, Unit](
+ cache,
+ map = SortedMap(
+ 1 -> new EntryProcessor(),
+ 3 -> new EntryProcessor()
+ )
+ ).args(2.expressionSuccess),
+ getAll[Int, Int](cache, SortedSet(1, 3))
+ .check(
+ entries[Int, Int].findAll.validate((entries: Seq[Entry[Int, Int]], _: Session) =>
+ entries.forall(e => e.value == 2 * (e.key + 1))
+ )
+ )
+ )
+
+ private def asyncOperationsWithCache(cache: String) = ignite(
+ putAll[Int, Int](cache, getBatch) async,
+ invokeAll[Int, Int, Unit](
+ cache,
+ map = SortedMap(
+ 1 -> new EntryProcessor(),
+ 3 -> new EntryProcessor()
+ )
+ ).args(2.expressionSuccess) async,
+ getAll[Int, Int](cache, SortedSet(1, 3))
+ .check(
+ entries[Int, Int].findAll.validate((entries: Seq[Entry[Int, Int]], _: Session) =>
+ entries.forall(e => e.value == 2 * (e.key + 1))
+ )
+ ) async
+ )
+
+ setUp(scn.inject(atOnceUsers(1)))
+ .protocols(protocol)
+ .assertions(
+ global.failedRequests.count.is(0)
+ )
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/test/scala/org/apache/ignite/gatling/InvokeTest.scala b/modules/gatling-ext/gatling-plugin/src/test/scala/org/apache/ignite/gatling/InvokeTest.scala
new file mode 100644
index 00000000..b249a37e
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/test/scala/org/apache/ignite/gatling/InvokeTest.scala
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling
+
+import java.util.concurrent.atomic.AtomicInteger
+
+import javax.cache.processor.MutableEntry
+
+import scala.language.postfixOps
+
+import com.typesafe.scalalogging.StrictLogging
+import io.gatling.core.Predef._
+import io.gatling.core.session.ExpressionSuccessWrapper
+import org.apache.ignite.gatling.InvokeTest.getValue
+import org.apache.ignite.gatling.Predef._
+import org.apache.ignite.gatling.Predef.group
+import org.apache.ignite.gatling.utils.AbstractGatlingTest
+import org.apache.ignite.gatling.utils.IgniteClientApi.NodeApi
+import org.apache.ignite.gatling.utils.IgniteSupport
+import org.junit.Test
+
+/**
+ * Tests invoke entry processor.
+ */
+class InvokeTest extends AbstractGatlingTest {
+ @Test
+ /** Tests entry processor without arguments. */
+ def noArgs(): Unit = runWith(NodeApi)(simulation = classOf[InvokeSimulation].getName)
+
+ @Test
+ /** Tests entry processor with additional arguments passed. */
+ def withArgs(): Unit = runWith(NodeApi)(simulation = classOf[InvokeArgsSimulation].getName)
+}
+
+object InvokeTest {
+ /** Helper to extract the fed value from session */
+ val getValue = "#{value}"
+}
+
+/**
+ * invoke entry processor without arguments simulation.
+ */
+class InvokeSimulation extends Simulation with IgniteSupport with StrictLogging {
+ private val atomicCache = "ATOMIC-TEST-CACHE"
+ private val transactionalCache = "TRANSACTIONAL-TEST-CACHE"
+
+ private val key = "1"
+ private val value = new AtomicInteger(0)
+
+ private val syncOperations = ignite(
+ put[String, Int](transactionalCache, key, getValue),
+ invoke[String, Int, Unit](transactionalCache, key) { e: MutableEntry[String, Int] =>
+ e.setValue(-e.getValue)
+ },
+ get[String, Int](transactionalCache, key)
+ check (
+ entries[String, Int].validate((e: Entry[String, Int], s: Session) => e.value == -s("value").as[Int]),
+ entries[String, Int].transform(-_.value).is(getValue)
+ )
+ )
+
+ private val asyncOperations = ignite(
+ put[String, Int](atomicCache, key, getValue) async,
+ invoke[String, Int, Unit](atomicCache, key) { e: MutableEntry[String, Int] =>
+ e.setValue(-e.getValue)
+ } async,
+ get[String, Int](atomicCache, key)
+ check (
+ entries[String, Int].validate((e: Entry[String, Int], s: Session) => e.value == -s("value").as[Int]),
+ entries[String, Int].transform(-_.value).is(getValue)
+ ) async
+ )
+
+ private val scn = scenario("invoke")
+ .feed(Iterator.continually(Map("value" -> value.incrementAndGet())))
+ .ignite(
+ getOrCreateCache(atomicCache),
+ group("run outside of transaction")(
+ asyncOperations
+ ),
+ getOrCreateCache(transactionalCache) atomicity TRANSACTIONAL,
+ tx run (
+ syncOperations
+ ) as "run in transaction"
+ )
+
+ setUp(scn.inject(atOnceUsers(1)))
+ .protocols(protocol)
+ .assertions(
+ global.failedRequests.count.is(0)
+ )
+}
+
+/**
+ * invoke entry processor with arguments simulation.
+ */
+class InvokeArgsSimulation extends Simulation with IgniteSupport with StrictLogging {
+ private val cache = "TEST-CACHE"
+
+ private val key = "1"
+ private val value = new AtomicInteger(0)
+
+ private val syncOperations = ignite(
+ put[String, Int](cache, key, getValue),
+ invoke[String, Int, Unit](cache, key).args(8.expressionSuccess) { (e: MutableEntry[String, Int], args: Seq[Any]) =>
+ e.setValue(e.getValue * args.head.asInstanceOf[Integer])
+ },
+ get[String, Int](cache, key)
+ check (
+ entries[String, Int].validate((e: Entry[String, Int], s: Session) =>
+ e.value == 8 * s("value").as[Int]
+ ),
+ entries[String, Int].transform(_.value / 8).is(getValue)
+ )
+ )
+
+ private val asyncOperations = ignite(
+ put[String, Int](cache, key, getValue) async,
+ invoke[String, Int, Unit](cache, key).args(8.expressionSuccess) { (e: MutableEntry[String, Int], args: Seq[Any]) =>
+ e.setValue(e.getValue * args.head.asInstanceOf[Integer])
+ } async,
+ get[String, Int](cache, key)
+ check (
+ entries[String, Int].validate((e: Entry[String, Int], s: Session) =>
+ e.value == 8 * s("value").as[Int]
+ ),
+ entries[String, Int].transform(_.value / 8).is(getValue)
+ ) async
+ )
+
+ private val scn = scenario("invoke")
+ .feed(Iterator.continually(Map("value" -> value.incrementAndGet())))
+ .ignite(
+ getOrCreateCache(cache) atomicity TRANSACTIONAL,
+ group("run outside of transaction")(
+ asyncOperations
+ ),
+ tx run (
+ syncOperations
+ ) as "run in transaction"
+ )
+
+ setUp(scn.inject(atOnceUsers(1)))
+ .protocols(protocol)
+ .assertions(
+ global.failedRequests.count.is(0)
+ )
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/test/scala/org/apache/ignite/gatling/LambdaTest.scala b/modules/gatling-ext/gatling-plugin/src/test/scala/org/apache/ignite/gatling/LambdaTest.scala
new file mode 100644
index 00000000..f34ce5f2
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/test/scala/org/apache/ignite/gatling/LambdaTest.scala
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling
+
+import scala.language.postfixOps
+
+import com.typesafe.scalalogging.StrictLogging
+import io.gatling.core.Predef._
+import io.gatling.core.session.Session
+import org.apache.ignite.Ignite
+import org.apache.ignite.client.IgniteClient
+import org.apache.ignite.gatling.LambdaTest.testCache
+import org.apache.ignite.gatling.Predef._
+import org.apache.ignite.gatling.utils.AbstractGatlingTest
+import org.apache.ignite.gatling.utils.IgniteClientApi.NodeApi
+import org.apache.ignite.gatling.utils.IgniteClientApi.ThinClient
+import org.apache.ignite.gatling.utils.IgniteSupport
+import org.junit.Test
+
+/**
+ * Tests an arbitrary lambda executing Ignite operations not exposed via the DSL.
+ */
+class LambdaTest extends AbstractGatlingTest {
+ /** Runs simulation with thin client. */
+ @Test
+ def thinClient(): Unit = runWith(ThinClient)(classOf[IgniteClientLambdaSimulation].getName)
+
+ /** Runs simulation with thick client. */
+ @Test
+ def thickClient(): Unit = runWith(NodeApi)(classOf[IgniteLambdaSimulation].getName)
+}
+
+object LambdaTest {
+ /** Test cache */
+ val testCache = "test-cache"
+}
+
+/**
+ */
+class IgniteLambdaSimulation extends Simulation with IgniteSupport with StrictLogging {
+
+ private val scn = scenario("scenario")
+ .feed(feeder)
+ .ignite { ignite: Ignite =>
+ ignite.createCache[Int, Int](testCache)
+ }
+ .ignite { (ignite: Ignite, session: Session) =>
+ val cache = ignite.cache[Int, Int](testCache)
+
+ cache.put(session("key").as[Int], session("value").as[Int])
+
+ val value = cache.get(session("key").as[Int])
+
+ if (value != session("value").as[Int]) {
+ throw new RuntimeException("get returns not the value which was put")
+ }
+ }
+ .ignite({ (ignite: Ignite, session: Session) =>
+ val cache = ignite.cache[Int, Int](testCache)
+
+ cache.put(session("key").as[Int], session("value").as[Int])
+
+ val value = cache.get(session("key").as[Int])
+
+ if (value != session("value").as[Int]) {
+ throw new RuntimeException("get returns not the value which was put")
+ }
+ } as "named")
+
+ setUp(scn.inject(atOnceUsers(1)))
+ .protocols(protocol)
+ .assertions(
+ global.failedRequests.count.is(0)
+ )
+}
+
+/**
+ */
+class IgniteClientLambdaSimulation extends Simulation with IgniteSupport with StrictLogging {
+
+ private val igniteClientOperations = (ignite: IgniteClient, session: Session) => {
+ val cache = ignite.cache[Int, Int](testCache)
+
+ cache.put(session("key").as[Int], session("value").as[Int])
+
+ cache.get(session("key").as[Int])
+
+ // simulate error in lambda
+ throw new RuntimeException("check failed in lambda")
+ }
+
+ private val scn = scenario("Basic")
+ .feed(feeder)
+ .ignite { ignite: IgniteClient =>
+ ignite.createCache[Int, Int](testCache)
+ }
+ .exec(
+ igniteClientOperations as "named"
+ )
+
+ setUp(scn.inject(atOnceUsers(1)))
+ .protocols(protocol)
+ .assertions(
+ global.failedRequests.count.is(1)
+ )
+}
diff --git a/modules/gatling-ext/gatling-plugin/src/test/scala/org/apache/ignite/gatling/LockTest.scala b/modules/gatling-ext/gatling-plugin/src/test/scala/org/apache/ignite/gatling/LockTest.scala
new file mode 100644
index 00000000..b605b000
--- /dev/null
+++ b/modules/gatling-ext/gatling-plugin/src/test/scala/org/apache/ignite/gatling/LockTest.scala
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.gatling
+
+import java.util.concurrent.atomic.AtomicInteger
+import java.util.concurrent.locks.Lock
+
+import javax.cache.processor.MutableEntry
+
+import scala.concurrent.duration.DurationInt
+import scala.language.postfixOps
+
+import com.typesafe.scalalogging.StrictLogging
+import io.gatling.core.Predef._
+import org.apache.ignite.gatling.LockTest.getValue
+import org.apache.ignite.gatling.Predef._
+import org.apache.ignite.gatling.utils.AbstractGatlingTest
+import org.apache.ignite.gatling.utils.IgniteClientApi.NodeApi
+import org.apache.ignite.gatling.utils.IgniteSupport
+import org.junit.Test
+
+/**
+ * Tests that locks work as expected.
+ */
+class LockTest extends AbstractGatlingTest {
+ /**
... 1571 lines suppressed ...