blob: f8b1ee75bbca786b1749b9ba52a480eb3d1b14a2 [file] [log] [blame]
/*
* 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)
}