Plugin allowing to run load tests against the Apache Ignite clusters using Gatling (3.10.x)
Plugin is currently available for Scala.
You may add plugin as dependency in project with your tests. Write this to your POM file:
<dependency> <groupId>org.apache.ignite</groupId> <artifactId>ignite-gatling-plugin</artifactId> <version>${version}</version> </dependency>
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
See here the example project based on the Gatling Maven Plugin.
The most popular Ignite operations are supported via the DSL which allows to define the test scenario in a way like below:
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.
val scn = scenario("PutAsync") .feed(feeder) .ignite( put[Int, Int]("cache", "#{key}", "#{value}") as "put" async )
The following operations are supported by the DSL:
See examples for each supported Ignite operations DSL in unit tests.
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.
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.
Connection to Ignite cluster is configured via the igniteProtocol
element. Several options are supported.
// 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:
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:
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:
If pool is used it should be closed in the simulation's after sections as:
val pool = new IgniteClientPerThreadPool( new ClientConfiguration().setAddresses("localhost:10800") ) val protocol: IgniteProtocol = igniteProtocol.clientPool(pool) after { pool.close() }