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

Fixes #16468: Computation of ComplianceLevel generates too many objects #2692

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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)))
}

}