-
Notifications
You must be signed in to change notification settings - Fork 73
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 #16382: Improve performance of policy generation writer #2666
Fixes #16382: Improve performance of policy generation writer #2666
Conversation
...r-core/src/main/scala/com/normation/rudder/services/policies/write/PolicyWriterService.scala
Show resolved
Hide resolved
...r-core/src/main/scala/com/normation/rudder/services/policies/write/PolicyWriterService.scala
Outdated
Show resolved
Hide resolved
PR rebased |
df0b5e2
to
a81193f
Compare
for { | ||
_ <- PolicyLoggerPure.trace(s"Loading template: ${templateId}") | ||
//string template does not allows "." in path name, so we are force to use a templateGroup by polity template (versions have . in them) | ||
content <- IOResult.effect(s"Error when copying technique template '${templateId.toString}'")(inputStream.asString(false)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is switching from IOUtils to better.files as a noticable impact ? if so, we could do that in 5.0 also
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so it seems that performance are no better
...r-core/src/main/scala/com/normation/rudder/services/policies/write/PolicyWriterService.scala
Outdated
Show resolved
Hide resolved
...r-core/src/main/scala/com/normation/rudder/services/policies/write/PolicyWriterService.scala
Show resolved
Hide resolved
...r-core/src/main/scala/com/normation/rudder/services/policies/write/PolicyWriterService.scala
Outdated
Show resolved
Hide resolved
val src = File(nodeFolder) | ||
if (src.isDirectory()) { | ||
val dest = File(backupFolder) | ||
if (dest.isDirectory) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just a stupid question: if the folder is there, and we move, do we really nned to delete first ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it was for the case when something bad happened in a previous generation. But now that we can be much more fine grained on our error management, perhaps we could delete on error. I'm not sur it changes much in perf (to avoid the unused delete).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
delete is fairly costly when there are many files and already a lors of I/O
I don't remember exactly, but it was between 2 and 6 minutes to delete with rm the backup folders with 10 000 nodes
...r-core/src/main/scala/com/normation/rudder/services/policies/write/PolicyWriterService.scala
Outdated
Show resolved
Hide resolved
PR updated with a new commit |
This fails:
i don't have more explaination than that |
...r-core/src/main/scala/com/normation/rudder/services/policies/write/PolicyWriterService.scala
Outdated
Show resolved
Hide resolved
def getHooks(basePath: String, ignoreSuffixes: List[String]): Box[Hooks] = getHooksPure(basePath, ignoreSuffixes).toBox | ||
|
||
def getHooksPure(basePath: String, ignoreSuffixes: List[String]): IOResult[Hooks] = { | ||
IOResult.effect { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it seems this causes a huge drop in performance
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I will need to test specifically that part. I think I spawn thread for non blocking part (the blocking part already spawn a thread via NuProcess. I need to check carefully
PR updated with a new commit |
2 similar comments
PR updated with a new commit |
PR updated with a new commit |
17fdfe1
to
7b1e301
Compare
PR updated with a new commit |
1 similar comment
PR updated with a new commit |
ae2282c
to
125afc6
Compare
8d1a42a
to
21a05f3
Compare
9c8178d
to
52b44bd
Compare
t <- pt.templatesToProcess | ||
} yield { | ||
(t.content, TemplateFillInfo(t.id, t.destination, p.paths.newFolder, pt.environmentVariables, pt.reportIdToReplace)) | ||
}).groupMap(_._1)(_._2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is really clever
) | ||
t2 <- currentTimeNanos | ||
_ <- writeTimer.writeTemplate.update(_ + t2 - t1) | ||
} yield () |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am a bit scared by the potential memory usage here, as we keep a lot of data in memory
I'm going to accept it, because it seems ok on our test platform, but let's keep an eye on this
*/ | ||
object FillTemplateThreadUnsafe { | ||
////////// Hottest method on whole Rudder ////////// | ||
def fill(templateName: String, sourceTemplate: StringTemplate, variables: Seq[STVariable], timer: FillTemplateTimer, replaceId: Option[(String, String)]): IOResult[(String, String)] = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is there 2 fill in this file ? how can we know which one to use ?
This PR is not mergeable to upper versions. |
OK, squash merging this PR |
52b44bd
to
5c4b3b2
Compare
https://issues.rudder.io/issues/16382
This PR try to make the abysmal performances of policy generation in 6.0 less abysmal.
Most of that PR was split in smaller amount, but there is a big part remaining: I change (most) of the the write logic, and that part can't really be done in smaller bit.
The big changes are:
It can be merded.
--- for records, what was done before --
Here, we explore three paths to earn some performances:
rules.new
torules
Box
toPureResult
andIOResult
. There is something extremely costly withtoIO
andtoBox
(and even more for layers of them).prepareTechniqueTemplate
Tests and measure were done on
WriteSystemTechniquesTest
, so the test case is rather specific and small (even for the 500 nodes cases). So the experiment should be replicated in real load testing environment to draw any conclusions.1/ That change does not seems to change anything, but my test case didn't exibited the big problems seen elsewhere. It could be an easy win.
2/ Changing the threadpool ergonomics leads to ~7% better perf, but it may be due to the very specific patterns in the unit test.
3/ This change is the biggest in number of lines. The most dramatic improvement is due to the changes in
STVariable
for validation, which drives on itself a ~7% improvement. Validation is called a lot of time. Other changes leads to 5-7% improvment.4/ This last change lead to ~30% improvment (but uniquely on
prepareTechniqueTemplate
phase)All in all, we get a consistant 20-22% improvment compared to 6.0.0.
--- oups, I did a rebase, I wanted to just do an added commit :/ ----
Some more change, and we are now near what we had in 5.0 (still 25% worse for writting techniques) but on the other hand, we are 50 times faster on
move promise to final position
.New changes:
Promise
for parsing template (to put it in cache) which allows to block less in semaphore,IOResult.effectNonBlocking
to minimize time in semaphore. This (especially avoiding thread creation witheffectNonBlocking
) leads to a 4x improvement on the most costly step.traversePar
). It leads to more stable result overall.It's also intersting to see that almost nothing change when we add nodes to the test (until we reach memory/gc limit, and then things go to a stop).
Results in images: