From 2acd89850a2ec0a12b168f5bc20edaa9934341aa Mon Sep 17 00:00:00 2001 From: Rick Parker Date: Thu, 2 Aug 2018 09:57:30 +0100 Subject: [PATCH] ENT-2198 Add more benchmarks to the overnight performance run (#1313) --- .../flows/CashPaymentFromKnownStatesFlow.kt | 65 +++++++++++++++++++ .../com/r3/corda/jmeter/BaseFlowSampler.kt | 5 +- .../kotlin/com/r3/corda/jmeter/Samplers.kt | 53 ++++++++++++++- .../resources/Testplans/NightlyBenchmark.jmx | 60 ++++++++--------- 4 files changed, 150 insertions(+), 33 deletions(-) create mode 100644 perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashPaymentFromKnownStatesFlow.kt diff --git a/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashPaymentFromKnownStatesFlow.kt b/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashPaymentFromKnownStatesFlow.kt new file mode 100644 index 0000000000..610cc41b0c --- /dev/null +++ b/perftestcordapp/src/main/kotlin/com/r3/corda/enterprise/perftestcordapp/flows/CashPaymentFromKnownStatesFlow.kt @@ -0,0 +1,65 @@ +package com.r3.corda.enterprise.perftestcordapp.flows + +import co.paralleluniverse.fibers.Suspendable +import com.r3.corda.enterprise.perftestcordapp.contracts.asset.Cash +import com.r3.corda.enterprise.perftestcordapp.contracts.asset.OnLedgerAsset +import com.r3.corda.enterprise.perftestcordapp.contracts.asset.PartyAndAmount +import net.corda.confidential.SwapIdentitiesFlow +import net.corda.core.contracts.Amount +import net.corda.core.contracts.Issued +import net.corda.core.contracts.StateRef +import net.corda.core.contracts.TransactionState +import net.corda.core.flows.StartableByRPC +import net.corda.core.identity.AbstractParty +import net.corda.core.identity.AnonymousParty +import net.corda.core.identity.Party +import net.corda.core.transactions.TransactionBuilder +import net.corda.core.utilities.ProgressTracker +import java.util.* + +@StartableByRPC +class CashPaymentFromKnownStatesFlow( + val inputs: Set, + val numberOfStates: Int, + val amountPerState: Amount, + val recipient: Party, + val anonymous: Boolean, + progressTracker: ProgressTracker) : AbstractCashFlow(progressTracker) { + + constructor(inputs: Set, + numberOfStates: Int, + amountPerState: Amount, + recipient: Party, + anonymous: Boolean) : this(inputs, numberOfStates, amountPerState, recipient, anonymous, tracker()) + + @Suspendable + override fun call(): AbstractCashFlow.Result { + fun deriveState(txState: TransactionState, amt: Amount>, owner: AbstractParty) = txState.copy(data = txState.data.copy(amount = amt, owner = owner)) + + progressTracker.currentStep = AbstractCashFlow.Companion.GENERATING_ID + val txIdentities = if (anonymous) { + subFlow(SwapIdentitiesFlow(recipient)) + } else { + emptyMap() + } + val anonymousRecipient = txIdentities[recipient] ?: recipient + progressTracker.currentStep = AbstractCashFlow.Companion.GENERATING_TX + val builder = TransactionBuilder(notary = null) + + val cashStateAndRef = inputs.map { serviceHub.toStateAndRef(it) } + val amounts = ArrayList(Collections.nCopies(numberOfStates, PartyAndAmount(anonymousRecipient, amountPerState))) + val changeIdentity = serviceHub.keyManagementService.freshKeyAndCert(ourIdentityAndCert, false) + + val (spendTx, keysForSigning) = OnLedgerAsset.generateSpend(builder, amounts, cashStateAndRef, + changeIdentity.party.anonymise(), + { state, quantity, owner -> deriveState(state, quantity, owner) }, + { Cash().generateMoveCommand() }) + + progressTracker.currentStep = AbstractCashFlow.Companion.SIGNING_TX + val tx = serviceHub.signInitialTransaction(spendTx, keysForSigning) + + progressTracker.currentStep = AbstractCashFlow.Companion.FINALISING_TX + val notarised = finaliseTx(tx, setOf(recipient), "Unable to notarise spend") + return AbstractCashFlow.Result(notarised.id, anonymousRecipient) + } +} \ No newline at end of file diff --git a/tools/jmeter/src/main/kotlin/com/r3/corda/jmeter/BaseFlowSampler.kt b/tools/jmeter/src/main/kotlin/com/r3/corda/jmeter/BaseFlowSampler.kt index b75cc4a49b..fb11f28f89 100644 --- a/tools/jmeter/src/main/kotlin/com/r3/corda/jmeter/BaseFlowSampler.kt +++ b/tools/jmeter/src/main/kotlin/com/r3/corda/jmeter/BaseFlowSampler.kt @@ -50,6 +50,8 @@ abstract class BaseFlowSampler : AbstractJavaSamplerClient() { private var labelValue: String? = null + protected open var flowResult: Any? = null + override fun getDefaultParameters(): Arguments { // Add copies of all args, since they seem to be mutable. return Arguments().apply { @@ -96,13 +98,14 @@ abstract class BaseFlowSampler : AbstractJavaSamplerClient() { result.sampleLabel = labelValue ?: flowInvoke.flowLogicClass.simpleName result.latencyEnd() try { - val flowResult = handle.returnValue.get() + flowResult = handle.returnValue.get() result.sampleEnd() result.apply { isSuccessful = true additionalFlowResponseProcessing(context, this, flowResult) } } catch (e: Exception) { + flowResult = null result.sampleEnd() e.printStackTrace() result.apply { diff --git a/tools/jmeter/src/main/kotlin/com/r3/corda/jmeter/Samplers.kt b/tools/jmeter/src/main/kotlin/com/r3/corda/jmeter/Samplers.kt index 5a62af2dcc..35a23cb819 100644 --- a/tools/jmeter/src/main/kotlin/com/r3/corda/jmeter/Samplers.kt +++ b/tools/jmeter/src/main/kotlin/com/r3/corda/jmeter/Samplers.kt @@ -13,10 +13,12 @@ package com.r3.corda.jmeter import com.r3.corda.enterprise.perftestcordapp.DOLLARS import com.r3.corda.enterprise.perftestcordapp.POUNDS import com.r3.corda.enterprise.perftestcordapp.flows.* +import net.corda.core.contracts.StateRef import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.messaging.CordaRPCOps import net.corda.core.utilities.OpaqueBytes +import net.corda.core.utilities.getOrThrow import org.apache.jmeter.config.Argument import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext import org.apache.jmeter.samplers.SampleResult @@ -90,8 +92,8 @@ class CashIssueSampler : AbstractSampler() { class CashIssueAndPaySampler : AbstractSampler() { companion object JMeterProperties { val otherParty = Argument("otherPartyName", "", "", "The X500 name of the payee.") - val coinSelection = Argument("useCoinSelection", "true", "", "True to use coin selection and false (or anything else) to avoid coin selection.") - val anonymousIdentities = Argument("anonymousIdentities", "true", "", "True to use anonymous identities and false (or anything else) to use well known identities.") + val coinSelection = Argument("useCoinSelection", "false", "", "True to use coin selection and false (or anything else) to avoid coin selection.") + val anonymousIdentities = Argument("anonymousIdentities", "false", "", "True to use anonymous identities and false (or anything else) to use well known identities.") } lateinit var counterParty: Party @@ -233,3 +235,50 @@ class LinearStateBatchNotariseSampler : AbstractSampler() { return result } } + +/** + * A sampler that issues cash once per sampler, and then generates a transaction to pay 1 dollar "numberOfStatesPerTx" times + * to a specified party per sample, thus invoking the notary and the payee via P2P. + * + * This allows us to test performance with different numbers of states per transaction, and to eliminate issuance from + * each sample (unlike CashIssueAndPaySampler). + */ +class CashPaySampler : AbstractSampler() { + companion object JMeterProperties { + val otherParty = Argument("otherPartyName", "", "", "The X500 name of the payee.") + val numberOfStatesPerTx = Argument("numberOfStatesPerTx", "1", "", "The number of payment states per transaction.") + val anonymousIdentities = Argument("anonymousIdentities", "false", "", "True to use anonymous identities and false (or anything else) to use well known identities.") + } + + lateinit var counterParty: Party + var numberOfStatesPerTxCount: Int = 1 + var useAnonymousIdentities: Boolean = true + private var inputIndex = 0 + + override fun setupTest(rpcProxy: CordaRPCOps, testContext: JavaSamplerContext) { + getNotaryIdentity(rpcProxy, testContext) + counterParty = getIdentity(rpcProxy, testContext, otherParty) + numberOfStatesPerTxCount = testContext.getParameter(numberOfStatesPerTx.name, numberOfStatesPerTx.value).toInt() + useAnonymousIdentities = testContext.getParameter(anonymousIdentities.name, anonymousIdentities.value).toBoolean() + + // Now issue lots of USD + val amount = 1_000_000_000.DOLLARS + val flowInvoke = FlowInvoke(CashIssueFlow::class.java, arrayOf(amount, OpaqueBytes.of(1), notaryIdentity)) + val handle = rpcProxy.startFlowDynamic(flowInvoke.flowLogicClass, *(flowInvoke.args)) + flowResult = handle.returnValue.getOrThrow() + } + + override fun createFlowInvoke(rpcProxy: CordaRPCOps, testContext: JavaSamplerContext): FlowInvoke<*> { + // Change is always Nth output + val input = StateRef((flowResult as AbstractCashFlow.Result).id, inputIndex) + val amount = 1.DOLLARS + inputIndex = numberOfStatesPerTxCount + return FlowInvoke(CashPaymentFromKnownStatesFlow::class.java, arrayOf(setOf(input), numberOfStatesPerTxCount, amount, counterParty, useAnonymousIdentities)) + } + + override fun teardownTest(rpcProxy: CordaRPCOps, testContext: JavaSamplerContext) { + } + + override val additionalArgs: Set + get() = setOf(notary, otherParty, numberOfStatesPerTx, anonymousIdentities) +} \ No newline at end of file diff --git a/tools/jmeter/src/main/resources/Testplans/NightlyBenchmark.jmx b/tools/jmeter/src/main/resources/Testplans/NightlyBenchmark.jmx index c8c57f096f..ad6d7a8173 100644 --- a/tools/jmeter/src/main/resources/Testplans/NightlyBenchmark.jmx +++ b/tools/jmeter/src/main/resources/Testplans/NightlyBenchmark.jmx @@ -61,7 +61,7 @@ - + continue false @@ -542,7 +542,7 @@ - + continue false @@ -649,9 +649,9 @@ ${otherPartyName} = - - useCoinSelection - false + + numberOfStatesPerTx + 1 = @@ -661,7 +661,7 @@ - com.r3.corda.jmeter.CashIssueAndPaySampler + com.r3.corda.jmeter.CashPaySampler @@ -718,9 +718,9 @@ ${otherPartyName} = - - useCoinSelection - false + + numberOfStatesPerTx + 1 = @@ -730,7 +730,7 @@ - com.r3.corda.jmeter.CashIssueAndPaySampler + com.r3.corda.jmeter.CashPaySampler @@ -787,9 +787,9 @@ ${otherPartyName} = - - useCoinSelection - false + + numberOfStatesPerTx + 1 = @@ -799,7 +799,7 @@ - com.r3.corda.jmeter.CashIssueAndPaySampler + com.r3.corda.jmeter.CashPaySampler @@ -856,9 +856,9 @@ ${otherPartyName} = - - useCoinSelection - false + + numberOfStatesPerTx + 1 = @@ -868,7 +868,7 @@ - com.r3.corda.jmeter.CashIssueAndPaySampler + com.r3.corda.jmeter.CashPaySampler @@ -925,9 +925,9 @@ ${otherPartyName} = - - useCoinSelection - false + + numberOfStatesPerTx + 1 = @@ -937,7 +937,7 @@ - com.r3.corda.jmeter.CashIssueAndPaySampler + com.r3.corda.jmeter.CashPaySampler @@ -994,9 +994,9 @@ ${otherPartyName} = - - useCoinSelection - false + + numberOfStatesPerTx + 1 = @@ -1006,7 +1006,7 @@ - com.r3.corda.jmeter.CashIssueAndPaySampler + com.r3.corda.jmeter.CashPaySampler @@ -1063,9 +1063,9 @@ ${otherPartyName} = - - useCoinSelection - false + + numberOfStatesPerTx + 1 = @@ -1075,7 +1075,7 @@ - com.r3.corda.jmeter.CashIssueAndPaySampler + com.r3.corda.jmeter.CashPaySampler