-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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
[Security Solution] Move calculation of rule source outside of applyRuleUpdate #199720
Conversation
With the current implementation, there are instances where we call `applyRuleUpdate` but do not want/need it to calculate rule source (e.g. when called from `importRules`, which pre-calculates the rule_source for incoming rules before passing them to `importRule`. Instead of adding a flag to conditionally call `calculateRuleSource` from within `applyRuleUpdate` I've opted to separate the two functions as these seem to be logically distinct actions. The three existing calls to `applyRuleUpdate` have been updated to be functionally equivalent. The effect of this PR is that we will no longer unnecessarily call `fetchAssetsByVersion` for each individual rule being imported, which should improve performance of rule import.
/ci |
Pinging @elastic/security-detection-rule-management (Team:Detection Rule Management) |
Pinging @elastic/security-detections-response (Team:Detections and Resp) |
Pinging @elastic/security-solution (Team: SecuritySolution) |
// If no override fields are provided, we calculate the rule source | ||
if (overrideFields == null) { | ||
ruleWithUpdates.rule_source = await calculateRuleSource({ | ||
rule: ruleWithUpdates, | ||
prebuiltRuleAssetClient, | ||
}); | ||
} else { | ||
ruleWithUpdates = { ...ruleWithUpdates, ...overrideFields }; | ||
} |
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 change raises some concerns about safety that I think we should discuss.
- Previously, updating a rule triggered a rule source recalculation, ensuring the rule source was always in sync with the rule content.
- Now, in some cases, we delegate the rule source recalculation to client users. If the
applyRuleUpdate
method usage isn’t closely monitored, we might see inconsistencies, with some rule sources being updated correctly while others might not.
This change seems to trade off implementation correctness for performance improvements, which could introduce potential issues. The root cause appears to be the splitting of the rule import logic between two clients: RuleSourceImporter
and DetectionRulesClient
. My suggestion is to adjust the DetectionRulesClient
's import method so the overrideFields
escape hatch is no longer necessary, and ensure that rule source recalculations are fully handled within a single method.
While performance improvements are important, it might be worth waiting until the rule customization feature flag is enabled by default before considering optimizations. This would also allow us to remove the legacy import method support and combine the RuleSourceImporter
and DetectionRulesClient
. But untill that, without concrete evidence that the performance is being impacted, premature optimizations might introduce more complexity than benefits.
@banderror, would love to hear your thoughts on this.
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.
Previously, updating a rule triggered a rule source recalculation, ensuring the rule source was always in sync with the rule content.
This is only true because we happen to call applyRuleUpdate
in those instances, right? From the perspective of the DetectionRulesClient
, nothing has changed here: #importRule
, #updateRule
, and upgradeRule
all follow the same logic as before, it's only the internal applyRuleUpdate
whose responsibility has changed. Are you arguing that applyRuleUpdate
needs to contain all of that logic?
Now, in some cases, we delegate the rule source recalculation to client users.
I see this as an optimization rather than an inconsistency: in the special case of importing rules, rule_source
is calculated in bulk as it's much more efficient.
If the general argument is that these extraneous calculations are acceptable/negligible: this code path is only hit when you're overwriting existing rules, which was about 45% slower than creating new rules in my recent testing. So: an edge case, but it's slower, but not to the point where it's timing out (even at 4000 rules). 🤷
@elasticmachine merge upstream |
@elasticmachine merge upstream |
💚 Build Succeeded
Metrics [docs]
History
cc @rylnd |
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 for the record will post my review, although in our meeting we decided to close it according to @xcrzx's recommendation.
LGTM! 👍 😆
@@ -43,10 +39,5 @@ export const applyRuleUpdate = async ({ | |||
created_by: existingRule.created_by, | |||
}; | |||
|
|||
nextRule.rule_source = await calculateRuleSource({ |
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.
Now that this call is removed, applyRuleUpdate
doesn't have to be async
anymore.
Closing this after some offline discussion: we concluded that the performance improvement gained here was not worth the decentralization of the rule source calculation. When the legacy import path is no longer needed, we can/should reexamine a similar optimization. |
Partially addresses: #195632
Summary
This is a small performance improvement that came out of this discussion on a previous PR. Note that the code in question is behind a feature flag (
prebuiltRulesCustomizationEnabled
). This issue relates to the Prebuilt Rule Import work, and its associated benchmarking effort.Context
With the current implementation, there are instances where we call
applyRuleUpdate
but do not want/need it to calculate rule source (e.g. when called fromimportRules
, which pre-calculates the rule_source for incoming rules before passing them toimportRule
.Instead of adding a flag to conditionally call
calculateRuleSource
from withinapplyRuleUpdate
I've opted to separate the two functions as these seem to be logically distinct actions.The three existing calls to
applyRuleUpdate
have been updated to be functionally equivalent.Effect
The effect of this PR is that we will no longer unnecessarily call
fetchAssetsByVersion
for each individual rule being imported, which should improve performance of rule import.For maintainers