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

[Security Solution] Move calculation of rule source outside of applyRuleUpdate #199720

Closed
wants to merge 6 commits into from

Conversation

rylnd
Copy link
Contributor

@rylnd rylnd commented Nov 11, 2024

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 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.

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

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.
@rylnd rylnd self-assigned this Nov 11, 2024
@rylnd rylnd added the release_note:skip Skip the PR/issue when compiling release notes label Nov 11, 2024
@rylnd
Copy link
Contributor Author

rylnd commented Nov 11, 2024

/ci

@rylnd rylnd added Team:Detection Rule Management Security Detection Rule Management Team Feature:Prebuilt Detection Rules Security Solution Prebuilt Detection Rules area Feature:Rule Import/Export Security Solution Detection Rule Import & Export workflow labels Nov 12, 2024
@rylnd rylnd marked this pull request as ready for review November 12, 2024 20:40
@rylnd rylnd requested a review from a team as a code owner November 12, 2024 20:40
@rylnd rylnd requested a review from jkelas November 12, 2024 20:40
@elasticmachine
Copy link
Contributor

Pinging @elastic/security-detection-rule-management (Team:Detection Rule Management)

@rylnd rylnd added the backport:skip This commit does not require backporting label Nov 12, 2024
@banderror banderror requested review from xcrzx and removed request for jkelas November 12, 2024 21:02
@banderror banderror added v9.0.0 backport:version Backport to applied version labels v8.17.0 and removed backport:skip This commit does not require backporting 8.17 candidate labels Nov 12, 2024
@banderror banderror self-requested a review November 13, 2024 14:47
@banderror banderror changed the title [Rule Management] Move calculation of rule source outside of applyRuleUpdate [Security Solution] Move calculation of rule source outside of applyRuleUpdate Nov 14, 2024
@banderror banderror added Team:Detections and Resp Security Detection Response Team Team: SecuritySolution Security Solutions Team working on SIEM, Endpoint, Timeline, Resolver, etc. labels Nov 14, 2024
@elasticmachine
Copy link
Contributor

Pinging @elastic/security-detections-response (Team:Detections and Resp)

@elasticmachine
Copy link
Contributor

Pinging @elastic/security-solution (Team: SecuritySolution)

Comment on lines +65 to +73
// 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 };
}
Copy link
Contributor

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.

Copy link
Contributor Author

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). 🤷

@banderror
Copy link
Contributor

@elasticmachine merge upstream

@banderror
Copy link
Contributor

@elasticmachine merge upstream

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

Metrics [docs]

✅ unchanged

History

cc @rylnd

Copy link
Contributor

@banderror banderror left a 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({
Copy link
Contributor

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.

@rylnd
Copy link
Contributor Author

rylnd commented Nov 27, 2024

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport:version Backport to applied version labels Feature:Prebuilt Detection Rules Security Solution Prebuilt Detection Rules area Feature:Rule Import/Export Security Solution Detection Rule Import & Export workflow release_note:skip Skip the PR/issue when compiling release notes Team:Detection Rule Management Security Detection Rule Management Team Team:Detections and Resp Security Detection Response Team Team: SecuritySolution Security Solutions Team working on SIEM, Endpoint, Timeline, Resolver, etc. v8.17.0 v9.0.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants