Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Command deduplication - test case that uses CommandService and CommandSubmissionService [KVL-1106] #11098

Merged
merged 4 commits into from
Oct 4, 2021
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Run mixed CommandService/CommandSubmissionService test for all the po…
…ssible combinations of calls
  • Loading branch information
nicu-da committed Oct 4, 2021
commit 9d8f2830e2514c7ffa78dec2c17bcf1e5a67cccf
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,12 @@ import com.daml.ledger.client.binding.Primitive.Party
import com.daml.ledger.test.model.DA.Types.Tuple2
import com.daml.ledger.test.model.Test.{Dummy, DummyWithAnnotation, TextKey, TextKeyOperations}
import com.daml.timer.Delayed
import io.grpc.Status
import io.grpc.Status.Code

import scala.annotation.nowarn
import scala.concurrent.duration._
import scala.concurrent.{ExecutionContext, Future}
import scala.util.{Failure, Random, Success}
import scala.util.{Failure, Success}

@nowarn("msg=deprecated")
private[testtool] abstract class CommandDeduplicationBase(
Expand Down Expand Up @@ -193,56 +192,69 @@ private[testtool] abstract class CommandDeduplicationBase(
}
)

testGivenAllParticipants(
s"${testNamingPrefix}SimpleDeduplicationMixedClients",
"Deduplicate commands within the deduplication time window using the command client and the command submission client",
allocate(SingleParty),
runConcurrently = false,
)(implicit ec =>
configuredParticipants => { case Participants(Participant(ledger, party)) =>
val submitAndWaitRequest = ledger
.submitAndWaitRequest(party, Dummy(party).create.command)
.update(
_.commands.deduplicationTime := deduplicationDuration.asProtobuf
)
val submitRequest = ledger
.submitRequest(party, Dummy(party).create.command)
.update(_.commands.commandId := submitAndWaitRequest.getCommands.commandId)
val firstSubmitAndWait = Random.nextBoolean()
runGivenDeduplicationWait(configuredParticipants) {
def submitAndAssertAccepted(submitAndWait: Boolean) = {
if (submitAndWait) ledger.submitAndWait(submitAndWaitRequest)
else submitRequestAndAssertCompletionAccepted(ledger)(submitRequest, party)
// Without the submission id we cannot assert the received completions for parallel submissions
if (deduplicationFeatures.appendOnlySchema)
testGivenAllParticipants(
s"${testNamingPrefix}SimpleDeduplicationMixedClients",
"Deduplicate commands within the deduplication time window using the command client and the command submission client",
allocate(SingleParty),
)(implicit ec =>
configuredParticipants => { case Participants(Participant(ledger, party)) =>
def combinations(elements: List[List[Boolean]]): List[List[Boolean]] = elements match {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚲 IIRC, this is a variation with repetitions, so to be more precise I would call it variations.

case Nil => List(Nil)
case currentElement :: tail =>
currentElement.flatMap(value => combinations(tail).map(value :: _))
}

def submitAndAssertDeduplicated(submitAndWait: Boolean) = {
if (submitAndWait)
submitAndWaitRequestAndAssertDeduplication(ledger)(submitAndWaitRequest)
else submitRequestAndAssertDeduplication(ledger)(submitRequest, party)
runGivenDeduplicationWait(configuredParticipants) { deduplicationWait =>
{
val numberOfCalls = 4
Future // cover all the different combinations of submit and submitAndWait
.traverse(combinations(List.fill(numberOfCalls)(List(true, false)))) {
case firstCall :: secondCall :: thirdCall :: fourthCall :: Nil =>
val submitAndWaitRequest = ledger
.submitAndWaitRequest(party, Dummy(party).create.command)
.update(
_.commands.deduplicationTime := deduplicationDuration.asProtobuf
)
val submitRequest = ledger
.submitRequest(party, Dummy(party).create.command)
.update(_.commands.commandId := submitAndWaitRequest.getCommands.commandId)
def submitAndAssertAccepted(submitAndWait: Boolean) = {
if (submitAndWait) ledger.submitAndWait(submitAndWaitRequest)
else
submitRequestAndAssertCompletionAccepted(ledger)(submitRequest, party)
.map(_ => ())
}
def submitAndAssertDeduplicated(submitAndWait: Boolean) = {
if (submitAndWait)
submitAndWaitRequestAndAssertDeduplication(ledger)(submitAndWaitRequest)
else submitRequestAndAssertDeduplication(ledger)(submitRequest, party)
}
for {
// Submit command (first deduplication window)
_ <- submitAndAssertAccepted(firstCall)
_ <- submitAndAssertDeduplicated(secondCall)

// Wait until the end of first deduplication window
_ <- Delayed.by(deduplicationWait)(())

// Submit command (second deduplication window)
_ <- submitAndAssertAccepted(thirdCall)
_ <- submitAndAssertDeduplicated(fourthCall)
} yield {}
case _ => throw new IllegalArgumentException("Wrong call list constructed")
}
.flatMap { _ =>
assertPartyHasActiveContracts(ledger)(
party = party,
noOfActiveContracts = 32, // 16 test cases, with 2 contracts per test case
)
}
}
}

deduplicationWait =>
for {
// Submit command (first deduplication window)
_ <- submitAndAssertAccepted(firstSubmitAndWait)
_ <- submitAndAssertDeduplicated(firstSubmitAndWait)

// Wait until the end of first deduplication window
_ <- Delayed.by(deduplicationWait)(())

// Submit command (second deduplication window)
_ <- submitAndAssertAccepted(!firstSubmitAndWait)
_ <- submitAndAssertDeduplicated(!firstSubmitAndWait)

// Inspect created contracts
_ <- assertPartyHasActiveContracts(ledger)(
party = party,
noOfActiveContracts = 2,
)
} yield {}
}
}
)
)

test(
s"${testNamingPrefix}DeduplicateSubmitterBasic",
"Commands with identical submitter and command identifier should be deduplicated by the submission client",
Expand Down Expand Up @@ -395,17 +407,18 @@ private[testtool] abstract class CommandDeduplicationBase(
val submissionId = UUID.randomUUID().toString
submitRequest(ledger)(request.update(_.commands.submissionId := submissionId))
.flatMap(ledgerEnd => {
ledger.firstCompletions(ledger.completionStreamRequest(ledgerEnd)(parties: _*))
if (deduplicationFeatures.appendOnlySchema)
// The [[Completion.submissionId]] is set only for append-only ledgers
ledger
.findCompletion(ledger.completionStreamRequest(ledgerEnd)(parties: _*))(
_.submissionId == submissionId
)
.map(_.toList)
else
ledger.firstCompletions(ledger.completionStreamRequest(ledgerEnd)(parties: _*))
})
.map { completions =>
val completion = assertSingleton("Expected only one completion", completions)
// The [[Completion.submissionId]] is set only for append-only ledgers
if (deduplicationFeatures.appendOnlySchema)
assert(
completion.submissionId == submissionId,
s"Submission id is different for completion. Completion has submission id [${completion.submissionId}], request has submission id [$submissionId]",
)
completion
assertSingleton("Expected only one completion", completions.toSeq)
}
}

Expand Down