Skip to content

Commit

Permalink
Fixes #16468: Computation of ComplianceLevel generates too many objects
Browse files Browse the repository at this point in the history
  • Loading branch information
ncharles committed Dec 31, 2019
1 parent 0ab7237 commit 56aa2f6
Show file tree
Hide file tree
Showing 2 changed files with 162 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -143,34 +143,117 @@ final case class ComplianceLevel(
object ComplianceLevel {
private def pc_for(i:Int, total:Int) : Double = if(total == 0) 0 else (i * 100 / BigDecimal(total)).setScale(2, BigDecimal.RoundingMode.HALF_UP).toDouble


def compute(reports: Iterable[ReportType]): ComplianceLevel = {
import ReportType._
if(reports.isEmpty) { ComplianceLevel(notApplicable = 1)}
else reports.foldLeft(ComplianceLevel()) { case (compliance, report) =>
report match {
case EnforceNotApplicable => compliance.copy(notApplicable = compliance.notApplicable + 1)
case EnforceSuccess => compliance.copy(success = compliance.success + 1)
case EnforceRepaired => compliance.copy(repaired = compliance.repaired + 1)
case EnforceError => compliance.copy(error = compliance.error + 1)
case Unexpected => compliance.copy(unexpected = compliance.unexpected + 1)
case Missing => compliance.copy(missing = compliance.missing + 1)
case NoAnswer => compliance.copy(noAnswer = compliance.noAnswer + 1)
case Pending => compliance.copy(pending = compliance.pending + 1)
case Disabled => compliance.copy(reportsDisabled = compliance.reportsDisabled + 1)
case AuditCompliant => compliance.copy(compliant = compliance.compliant + 1)
case AuditNotApplicable => compliance.copy(auditNotApplicable = compliance.auditNotApplicable + 1)
case AuditNonCompliant => compliance.copy(nonCompliant = compliance.nonCompliant + 1)
case AuditError => compliance.copy(auditError = compliance.auditError + 1)
case BadPolicyMode => compliance.copy(badPolicyMode = compliance.badPolicyMode + 1)
if(reports.isEmpty) {
ComplianceLevel(notApplicable = 1)
} else {
var notApplicable = 0
var success = 0
var repaired = 0
var error = 0
var unexpected = 0
var missing = 0
var noAnswer = 0
var pending = 0
var reportsDisabled = 0
var compliant = 0
var auditNotApplicable = 0
var nonCompliant = 0
var auditError = 0
var badPolicyMode = 0

reports.foreach { report =>
report match {
case EnforceNotApplicable => notApplicable += 1
case EnforceSuccess => success += 1
case EnforceRepaired => repaired += 1
case EnforceError => error += 1
case Unexpected => unexpected += 1
case Missing => missing += 1
case NoAnswer => noAnswer += 1
case Pending => pending += 1
case Disabled => reportsDisabled += 1
case AuditCompliant => compliant += 1
case AuditNotApplicable => auditNotApplicable += 1
case AuditNonCompliant => nonCompliant += 1
case AuditError => auditError += 1
case BadPolicyMode => badPolicyMode += 1
}
}
ComplianceLevel(
pending = pending
, success = success
, repaired = repaired
, error =error
, unexpected =unexpected
, missing = missing
, noAnswer=noAnswer
, notApplicable=notApplicable
, reportsDisabled=reportsDisabled
, compliant = compliant
, auditNotApplicable= auditNotApplicable
, nonCompliant =nonCompliant
, auditError = auditError
, badPolicyMode =badPolicyMode
)
}
}

def sum(compliances: Iterable[ComplianceLevel]): ComplianceLevel = {
if(compliances.isEmpty) ComplianceLevel()
else compliances.reduce( _ + _)
}
def sum(compliances: Iterable[ComplianceLevel]): ComplianceLevel = {
if (compliances.isEmpty) {
ComplianceLevel()
} else {
var pending: Int = 0
var success: Int = 0
var repaired: Int = 0
var error: Int = 0
var unexpected: Int = 0
var missing: Int = 0
var noAnswer: Int = 0
var notApplicable: Int = 0
var reportsDisabled: Int = 0
var compliant: Int = 0
var auditNotApplicable: Int = 0
var nonCompliant: Int = 0
var auditError: Int = 0
var badPolicyMode: Int = 0


compliances.foreach { compliance =>
pending += compliance.pending
success += compliance.success
repaired += compliance.repaired
error += compliance.error
unexpected += compliance.unexpected
missing += compliance.missing
noAnswer += compliance.noAnswer
notApplicable += compliance.notApplicable
reportsDisabled += compliance.reportsDisabled
compliant += compliance.compliant
auditNotApplicable += compliance.auditNotApplicable
nonCompliant += compliance.nonCompliant
auditError += compliance.auditError
badPolicyMode += compliance.badPolicyMode
}
ComplianceLevel(
pending = pending
, success = success
, repaired = repaired
, error = error
, unexpected = unexpected
, missing = missing
, noAnswer = noAnswer
, notApplicable = notApplicable
, reportsDisabled = reportsDisabled
, compliant = compliant
, auditNotApplicable = auditNotApplicable
, nonCompliant = nonCompliant
, auditError = auditError
, badPolicyMode = badPolicyMode
)
}
}
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class StatusReportTest extends Specification {
private[this] implicit def r2n(s: String): RuleId = RuleId(s)
private[this] implicit def d2n(s: String): DirectiveId = DirectiveId(s)

sequential

"Compliance" should {

Expand Down Expand Up @@ -270,6 +271,53 @@ class StatusReportTest extends Specification {
report.byNodes(NodeId("n3")).compliance === ComplianceLevel(error = 1)
}

"performance for compute ReportType" should {
val nbSet = 90
val sizeSet = 100

// force tests to be sequentials

val initData = buildComplianceLevelSets(nbSet,sizeSet)

"init correctly" in {
val result = initData.headOption.map(x => ComplianceLevel.compute(x._2))

val comp = ComplianceLevel.sum(Seq(result.get))
result.size === 1
}

"run fast enough to compute" in {
initData.map( x => ComplianceLevel.compute(x._2))
val t0 = System.nanoTime

for (i <- 1 to 100) {
val t0_0 = System.nanoTime
initData.map(x => ComplianceLevel.compute(x._2))
val t1_1 = System.nanoTime
println(s"${i}th call to compute for ${nbSet} sets took ${(t1_1-t0_0)/1000} µs")
}
val t1 = System.nanoTime
println(s"Time to run test is ${(t1-t0)/1000} µs")
(t1-t0) must be lessThan( 200000*1000 ) // tests show 60030µs
}

"run fast enough to sum" in {
ComplianceLevel.sum(initData.map( x => ComplianceLevel.compute(x._2)))

val source = initData.map(x => (x._1, ComplianceLevel.compute(x._2))).map( _._2)
val t0 = System.nanoTime

for (i <- 1 to 100) {
val t0_0 = System.nanoTime
val result = ComplianceLevel.sum(source)
val t1_1 = System.nanoTime
println(s"${i}th call to sum for ${nbSet} sets took ${(t1_1-t0_0)/1000} µs")
}
val t1 = System.nanoTime
println(s"Time to run test for sum is ${(t1-t0)/1000} µs")
(t1-t0) must be lessThan( 20000*1000 ) // tests show 3159µs
}
}
}

private[this] def parse(s: String): Seq[RuleNodeStatusReport] = {
Expand Down Expand Up @@ -317,5 +365,14 @@ class StatusReportTest extends Specification {
AggregatedStatusReport(nr)
}

def buildComplianceLevelSets(
nbSet: Int
, sizeSet: Int
) = {
val sets = (1 to nbSet).toSeq
val reportTypes: Seq[ReportType] = (1 to (sizeSet/2)).map(_ => EnforceSuccess).toSeq ++ ((1 to (sizeSet/2)).map(_ => EnforceRepaired).toSeq)

sets.map( x => (x, reportTypes ++ (1 to x).map(_ => EnforceNotApplicable)))
}

}

0 comments on commit 56aa2f6

Please sign in to comment.