TransactionNodesStatistics.stats to return case class of same name (#12159)

* TransactionNodesStatistics.stats now returns case class of same name

changelog_begin
changelog_end

* Rename TransactionNodesStats to Detail and make inner class

* Rename TransactionNodesStatistics.stats to apply

* Rename TransactionNodesStatistics to TransactionNodeStatistics

* Reformat

* Remove rollback from statistic action count

* Update with review comments

* Update with review comments
This commit is contained in:
Simon Maxen 2021-12-17 15:27:53 +00:00 committed by GitHub
parent 0a2c207e93
commit 51b9405f5c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 200 additions and 179 deletions

View File

@ -0,0 +1,152 @@
// Copyright (c) 2021 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.lf
package transaction
object TransactionNodeStatistics {
/** Container for transaction statistics.
*
* @param creates number of creates nodes,
* @param consumingExercisesByCid number of consuming exercises by contract ID nodes,
* @param nonconsumingExercisesByCid number of non-consuming Exercises by contract ID nodes,
* @param consumingExercisesByKey number of consuming exercise by contract key nodes,
* @param nonconsumingExercisesByKey number of non-consuming exercise by key nodes,
* @param fetchesByCid number of fetch by contract ID nodes,
* @param fetchesByKey number of fetch by key nodes,
* @param lookupsByKey number of lookup by key nodes,
*/
final case class Actions(
creates: Int,
consumingExercisesByCid: Int,
nonconsumingExercisesByCid: Int,
consumingExercisesByKey: Int,
nonconsumingExercisesByKey: Int,
fetchesByCid: Int,
fetchesByKey: Int,
lookupsByKey: Int,
) {
def +(that: Actions) =
Actions(
creates = this.creates + that.creates,
consumingExercisesByCid = this.consumingExercisesByCid + that.consumingExercisesByCid,
nonconsumingExercisesByCid =
this.nonconsumingExercisesByCid + that.nonconsumingExercisesByCid,
consumingExercisesByKey = this.consumingExercisesByKey + that.consumingExercisesByKey,
nonconsumingExercisesByKey =
this.nonconsumingExercisesByKey + that.nonconsumingExercisesByKey,
fetchesByCid = this.fetchesByCid + that.fetchesByCid,
fetchesByKey = this.fetchesByKey + that.fetchesByKey,
lookupsByKey = this.lookupsByKey + that.lookupsByKey,
)
def exercisesByCid: Int = consumingExercisesByCid + nonconsumingExercisesByCid
def exercisesByKey: Int = consumingExercisesByKey + nonconsumingExercisesByKey
def exercises: Int = exercisesByCid + exercisesByKey
def consumingExercises: Int = consumingExercisesByCid + consumingExercisesByKey
def nonconsumingExercises: Int = nonconsumingExercisesByCid + nonconsumingExercisesByKey
def fetches: Int = fetchesByCid + fetchesByKey
def byKeys: Int = exercisesByKey + fetchesByKey + lookupsByKey
def actions: Int = creates + exercises + fetches + lookupsByKey
}
val EmptyActions: Actions = Actions(0, 0, 0, 0, 0, 0, 0, 0)
val Empty: TransactionNodeStatistics = TransactionNodeStatistics(EmptyActions, EmptyActions)
private[this] val numberOfFields = EmptyActions.productArity
private[this] val Seq(
createsIdx,
consumingExercisesByCidIdx,
nonconsumingExerciseCidsIdx,
consumingExercisesByKeyIdx,
nonconsumingExercisesByKeyIdx,
fetchesIdx,
fetchesByKeyIdx,
lookupsByKeyIdx,
) =
(0 until numberOfFields)
private[this] def emptyFields = Array.fill(numberOfFields)(0)
private[this] def build(stats: Array[Int]) =
Actions(
creates = stats(createsIdx),
consumingExercisesByCid = stats(consumingExercisesByCidIdx),
nonconsumingExercisesByCid = stats(nonconsumingExerciseCidsIdx),
consumingExercisesByKey = stats(consumingExercisesByKeyIdx),
nonconsumingExercisesByKey = stats(nonconsumingExercisesByKeyIdx),
fetchesByCid = stats(fetchesIdx),
fetchesByKey = stats(fetchesByKeyIdx),
lookupsByKey = stats(lookupsByKeyIdx),
)
/** This function produces statistics about the committed nodes (those nodes
* that do not appear under a rollback node) on the one hand and
* rolled back nodes (those nodes that do appear under a rollback node) on
* the other hand within a given transaction `tx`.
*/
def apply(tx: VersionedTransaction): TransactionNodeStatistics =
apply(tx.transaction)
def apply(tx: Transaction): TransactionNodeStatistics = {
val committed = emptyFields
val rolledBack = emptyFields
var rollbackDepth = 0
def incr(fieldIdx: Int) =
if (rollbackDepth > 0) rolledBack(fieldIdx) += 1 else committed(fieldIdx) += 1
tx.foreachInExecutionOrder(
exerciseBegin = { (_, exe) =>
val idx =
if (exe.consuming)
if (exe.byKey)
consumingExercisesByKeyIdx
else
consumingExercisesByCidIdx
else if (exe.byKey)
nonconsumingExercisesByKeyIdx
else
nonconsumingExerciseCidsIdx
incr(idx)
Transaction.ChildrenRecursion.DoRecurse
},
rollbackBegin = { (_, _) =>
rollbackDepth += 1
Transaction.ChildrenRecursion.DoRecurse
},
leaf = { (_, node) =>
val idx = node match {
case _: Node.Create =>
createsIdx
case fetch: Node.Fetch =>
if (fetch.byKey)
fetchesByKeyIdx
else
fetchesIdx
case _: Node.LookupByKey =>
lookupsByKeyIdx
}
incr(idx)
},
exerciseEnd = (_, _) => (),
rollbackEnd = (_, _) => rollbackDepth -= 1,
)
TransactionNodeStatistics(build(committed), build(rolledBack))
}
}
final case class TransactionNodeStatistics(
committed: TransactionNodeStatistics.Actions,
rolledBack: TransactionNodeStatistics.Actions,
) {
def +(that: TransactionNodeStatistics) = {
TransactionNodeStatistics(this.committed + that.committed, this.rolledBack + that.rolledBack)
}
}

View File

@ -1,148 +0,0 @@
// Copyright (c) 2021 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.lf
package transaction
/** Container for transaction statistics.
*
* @param creates number of creates nodes,
* @param consumingExercisesByCid number of consuming exercises by contract ID nodes,
* @param nonconsumingExercisesByCid number of non-consuming Exercises by contract ID nodes,
* @param consumingExercisesByKey number of consuming exercise by contract key nodes,
* @param nonconsumingExercisesByKey number of non-consuming exercise by key nodes,
* @param fetchesByCid number of fetch by contract ID nodes,
* @param fetchesByKey number of fetch by key nodes,
* @param lookupsByKey number of lookup by key nodes,
* @param rollbacks number of rollback nodes.
*/
final case class TransactionNodesStatistics(
creates: Int,
consumingExercisesByCid: Int,
nonconsumingExercisesByCid: Int,
consumingExercisesByKey: Int,
nonconsumingExercisesByKey: Int,
fetchesByCid: Int,
fetchesByKey: Int,
lookupsByKey: Int,
rollbacks: Int,
) {
def +(that: TransactionNodesStatistics) =
TransactionNodesStatistics(
creates = this.creates + that.creates,
consumingExercisesByCid = this.consumingExercisesByCid + that.consumingExercisesByCid,
nonconsumingExercisesByCid =
this.nonconsumingExercisesByCid + that.nonconsumingExercisesByCid,
consumingExercisesByKey = this.consumingExercisesByKey + that.consumingExercisesByKey,
nonconsumingExercisesByKey =
this.nonconsumingExercisesByKey + that.nonconsumingExercisesByKey,
fetchesByCid = this.fetchesByCid + that.fetchesByCid,
fetchesByKey = this.fetchesByKey + that.fetchesByKey,
lookupsByKey = this.lookupsByKey + that.lookupsByKey,
rollbacks = this.rollbacks + that.rollbacks,
)
def exercisesByCid: Int = consumingExercisesByCid + nonconsumingExercisesByCid
def exercisesByKey: Int = consumingExercisesByKey + nonconsumingExercisesByKey
def exercises: Int = exercisesByCid + exercisesByKey
def consumingExercises: Int = consumingExercisesByCid + consumingExercisesByKey
def nonconsumingExercises: Int = nonconsumingExercisesByCid + nonconsumingExercisesByKey
def fetches: Int = fetchesByCid + fetchesByKey
def byKeys: Int = exercisesByKey + fetchesByKey + lookupsByKey
def actions: Int = creates + exercises + fetches + lookupsByKey
def nodes: Int = actions + rollbacks
}
object TransactionNodesStatistics {
val Empty = TransactionNodesStatistics(0, 0, 0, 0, 0, 0, 0, 0, 0)
private[this] val numberOfFields = Empty.productArity
private[this] val Seq(
createsIdx,
consumingExercisesByCidIdx,
nonconsumingExerciseCidsIdx,
consumingExercisesByKeyIdx,
nonconsumingExercisesByKeyIdx,
fetchesIdx,
fetchesByKeyIdx,
lookupsByKeyIdx,
rollbacksIdx,
) =
(0 until numberOfFields)
private[this] def emptyFields = Array.fill(numberOfFields)(0)
private[this] def build(stats: Array[Int]) =
TransactionNodesStatistics(
creates = stats(createsIdx),
consumingExercisesByCid = stats(consumingExercisesByCidIdx),
nonconsumingExercisesByCid = stats(nonconsumingExerciseCidsIdx),
consumingExercisesByKey = stats(consumingExercisesByKeyIdx),
nonconsumingExercisesByKey = stats(nonconsumingExercisesByKeyIdx),
fetchesByCid = stats(fetchesIdx),
fetchesByKey = stats(fetchesByKeyIdx),
lookupsByKey = stats(lookupsByKeyIdx),
rollbacks = stats(rollbacksIdx),
)
/** This function produces statistics about the committed nodes (those nodes
* that do not appear under a rollback node) on the one hand and
* rolled back nodes (those nodes that do appear under a rollback node) on
* the other hand within a given transaction `tx`.
*/
def stats(tx: VersionedTransaction): (TransactionNodesStatistics, TransactionNodesStatistics) =
stats(tx.transaction)
def stats(tx: Transaction): (TransactionNodesStatistics, TransactionNodesStatistics) = {
val committed = emptyFields
val rolledBack = emptyFields
var rollbackDepth = 0
def incr(fieldIdx: Int) =
if (rollbackDepth > 0) rolledBack(fieldIdx) += 1 else committed(fieldIdx) += 1
tx.foreachInExecutionOrder(
exerciseBegin = { (_, exe) =>
val idx =
if (exe.consuming)
if (exe.byKey)
consumingExercisesByKeyIdx
else
consumingExercisesByCidIdx
else if (exe.byKey)
nonconsumingExercisesByKeyIdx
else
nonconsumingExerciseCidsIdx
incr(idx)
Transaction.ChildrenRecursion.DoRecurse
},
rollbackBegin = { (_, _) =>
incr(rollbacksIdx)
rollbackDepth += 1
Transaction.ChildrenRecursion.DoRecurse
},
leaf = { (_, node) =>
val idx = node match {
case _: Node.Create =>
createsIdx
case fetch: Node.Fetch =>
if (fetch.byKey)
fetchesByKeyIdx
else
fetchesIdx
case _: Node.LookupByKey =>
lookupsByKeyIdx
}
incr(idx)
},
exerciseEnd = (_, _) => (),
rollbackEnd = (_, _) => rollbackDepth -= 1,
)
(build(committed), build(rolledBack))
}
}

View File

@ -4,6 +4,7 @@
package com.daml.lf
package transaction
import com.daml.lf.transaction.TransactionNodeStatistics.Actions
import com.daml.lf.transaction.test.{TransactionBuilder => TxBuilder}
import com.daml.lf.value.Value
import org.scalatest.Inside
@ -11,29 +12,44 @@ import org.scalatest.matchers.should.Matchers
import org.scalatest.prop.TableDrivenPropertyChecks
import org.scalatest.wordspec.AnyWordSpec
class TransactionNodesStatisticsSpec
class TransactionNodeStatisticsSpec
extends AnyWordSpec
with Inside
with Matchers
with TableDrivenPropertyChecks {
"TransactionNodeStatistics#+" should {
"TransactionNodeStatistics.Actions#+" should {
"add" in {
val s1 = TransactionNodesStatistics(1, 1, 1, 1, 1, 1, 1, 1, 1)
val s2 = TransactionNodesStatistics(2, 3, 5, 7, 11, 13, 17, 19, 23)
val s1s2 = TransactionNodesStatistics(3, 4, 6, 8, 12, 14, 18, 20, 24)
val s2s2 = TransactionNodesStatistics(4, 6, 10, 14, 22, 26, 34, 38, 46)
val s1 = Actions(1, 1, 1, 1, 1, 1, 1, 1)
val s2 = Actions(2, 3, 5, 7, 11, 13, 17, 19)
val s1s2 = Actions(3, 4, 6, 8, 12, 14, 18, 20)
val s2s2 = Actions(4, 6, 10, 14, 22, 26, 34, 38)
s2 + TransactionNodesStatistics.Empty shouldBe s2
TransactionNodesStatistics.Empty + s2 shouldBe s2
s2 + TransactionNodeStatistics.EmptyActions shouldBe s2
TransactionNodeStatistics.EmptyActions + s2 shouldBe s2
s1 + s2 shouldBe s1s2
s2 + s1 shouldBe s1s2
s2 + s2 shouldBe s2s2
}
}
"TransactionNodeStatistics.stats" should {
"TransactionNodeStatistics#+" should {
"add" in {
val d1c = Actions(1, 1, 1, 1, 1, 1, 1, 1)
val d1r = Actions(2, 3, 5, 7, 11, 13, 17, 19)
val d2c = Actions(3, 5, 7, 11, 13, 17, 19, 23)
val d2r = Actions(5, 7, 11, 13, 17, 19, 23, 29)
val s1 = TransactionNodeStatistics(d1c, d1r)
val s2 = TransactionNodeStatistics(d2c, d2r)
val expected = TransactionNodeStatistics(d1c + d2c, d1r + d2r)
s1 + s2 shouldBe expected
}
}
"TransactionNodeStatistics" should {
def create(b: TxBuilder, withKey: Boolean = false) = {
val parties = Set(b.newParty)
@ -88,7 +104,7 @@ class TransactionNodesStatisticsSpec
val testIterations = 3
type Getter = TransactionNodesStatistics => Int
type Getter = Actions => Int
val testCases = Table[TxBuilder => Node, Getter](
"makeNode" -> "getter",
@ -100,7 +116,6 @@ class TransactionNodesStatisticsSpec
(fetch(byKey = false), _.fetchesByCid),
(fetch(byKey = true), _.fetchesByKey),
(lookup, _.lookupsByKey),
(rollback, _.rollbacks),
)
"count each type of committed nodes properly" in {
@ -109,11 +124,11 @@ class TransactionNodesStatisticsSpec
for (i <- 1 to testIterations) {
builder.add(makeNode(builder))
inside(TransactionNodesStatistics.stats(builder.build())) {
case (committed, rolledBack) =>
inside(TransactionNodeStatistics(builder.build())) {
case TransactionNodeStatistics(committed, rolledBack) =>
getter(committed) shouldBe i
committed.nodes shouldBe i
rolledBack shouldBe TransactionNodesStatistics.Empty
committed.actions shouldBe i
rolledBack shouldBe TransactionNodeStatistics.EmptyActions
}
}
}
@ -126,11 +141,11 @@ class TransactionNodesStatisticsSpec
for (i <- 1 to testIterations) {
builder.add(makeNode(builder), rollbackId)
inside(TransactionNodesStatistics.stats(builder.build())) {
case (committed, rolledBack) =>
committed shouldBe TransactionNodesStatistics(0, 0, 0, 0, 0, 0, 0, 0, rollbacks = 1)
inside(TransactionNodeStatistics.apply(builder.build())) {
case TransactionNodeStatistics(committed, rolledBack) =>
committed shouldBe Actions(0, 0, 0, 0, 0, 0, 0, 0)
getter(rolledBack) shouldBe i
rolledBack.nodes shouldBe i
rolledBack.actions shouldBe i
}
}
}
@ -142,11 +157,12 @@ class TransactionNodesStatisticsSpec
for (i <- 1 to testIterations) {
addAllNodes(b, exeId) // one additional nodes of each types
inside(TransactionNodesStatistics.stats(b.build())) { case (committed, rolledBack) =>
// There are twice more nonconsumming exercises by cid are double because
// we use a extra one to nest the other node of the next loop
committed shouldBe TransactionNodesStatistics(i, i, 2 * i, i, i, i, i, i, i)
rolledBack shouldBe TransactionNodesStatistics.Empty
inside(TransactionNodeStatistics.apply(b.build())) {
case TransactionNodeStatistics(committed, rolledBack) =>
// There are twice more nonconsumming exercises by cid are double because
// we use a extra one to nest the other node of the next loop
committed shouldBe Actions(i, i, 2 * i, i, i, i, i, i)
rolledBack shouldBe TransactionNodeStatistics.EmptyActions
}
exeId = b.add(exe(false, false)(b), exeId) // one nonconsumming exercises
}
@ -159,11 +175,12 @@ class TransactionNodesStatisticsSpec
for (i <- 1 to testIterations) {
rbId = b.add(rollback(b), rbId) // one additional rolled Back rollback node
addAllNodes(b, rbId) // one additional rolled Back nodes of each type
inside(TransactionNodesStatistics.stats(b.build())) { case (committed, rolledBack) =>
committed shouldBe TransactionNodesStatistics(0, 0, 0, 0, 0, 0, 0, 0, 1)
// There are twice more rollback nodes, since we use an extra one to
// nest the other nodes in each loop
rolledBack shouldBe TransactionNodesStatistics(i, i, i, i, i, i, i, i, 2 * i)
inside(TransactionNodeStatistics.apply(b.build())) {
case TransactionNodeStatistics(committed, rolledBack) =>
committed shouldBe Actions(0, 0, 0, 0, 0, 0, 0, 0)
// There are twice more rollback nodes, since we use an extra one to
// nest the other nodes in each loop
rolledBack shouldBe Actions(i, i, i, i, i, i, i, i)
}
}
}

View File

@ -5,7 +5,7 @@ package com.daml.ledger.participant.state.v2
import com.daml.ledger.api.DeduplicationPeriod
import com.daml.lf.data.Ref
import com.daml.lf.transaction.TransactionNodesStatistics
import com.daml.lf.transaction.TransactionNodeStatistics
import com.daml.logging.entries.{LoggingValue, ToLoggingValue}
/** Information about a completion for a submission.
@ -43,7 +43,7 @@ case class CompletionInfo(
commandId: Ref.CommandId,
optDeduplicationPeriod: Option[DeduplicationPeriod],
submissionId: Option[Ref.SubmissionId],
statistics: Option[TransactionNodesStatistics],
statistics: Option[TransactionNodeStatistics],
) {
def changeId: ChangeId = ChangeId(applicationId, commandId, actAs.toSet)
}