-
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] Update in-memory rules table implementation feature switch logic #124043
Conversation
dbbb3a9
to
6aaa382
Compare
6aaa382
to
c3afce6
Compare
Pinging @elastic/security-detections-response (Team:Detections and Resp) |
Pinging @elastic/security-solution (Team: SecuritySolution) |
grid-template-columns: 1fr auto; | ||
align-items: center; | ||
grid-gap: 16px; | ||
box-shadow: inset 0 -1px 0 #d3dae6; |
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.
can we use color from euiTheme?
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.
Yes, sure. I'll change that in a follow-up
We use |
/** | ||
* Use this method to update rules data cached by react-query. | ||
* It is useful when we receive new rules back from a mutation query (bulk edit, etc.); | ||
* we can merge those rules with the existing cache to avoid an extra roundtrip to re-fetch updated rules. | ||
*/ | ||
const updateRules = useCallback( | ||
(newRules: Rule[]) => { | ||
queryClient.setQueryData<ReturnType<typeof useFindRules>['data']>( | ||
getFindRulesQueryKey({ isInMemorySorting, filterOptions, sortingOptions, pagination }), | ||
(currentData) => ({ | ||
rules: mergeRules(currentData?.rules || [], newRules), | ||
total: currentData?.total || 0, | ||
}) | ||
); | ||
|
||
/** | ||
* Unset loading state for all new rules | ||
*/ | ||
const newRuleIds = newRules.map((r) => r.id); | ||
const newLoadingRuleIds = loadingRules.ids.filter((id) => !newRuleIds.includes(id)); | ||
setLoadingRules({ | ||
ids: newLoadingRuleIds, | ||
action: newLoadingRuleIds.length === 0 ? null : loadingRules.action, | ||
}); | ||
}, |
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 my only concern with this PR is that updating the cache of loaded rules has been replaced by cache invalidation. I'd expect that in the in-memory mode, this can lead to continuous reloading of all the rules when the user is actively working with the table (e.g. enabling/disabling rules, editing tags, etc).
What was the reason for this change?
If the reason is that now we have two caches that are maintained simultaneously (allRules
and pagedRules
in useFindRules
), could we change the implementation of updateRules
so that it updates both of them?
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'd expect that in the in-memory mode, this can lead to continuous reloading of all the rules when the user is actively working with the table (e.g. enabling/disabling rules, editing tags, etc).
This change does not affect editing operations because they never had optimistic updates. Therefore, only rule (de)activation is affected. So while cache invalidation is not an ideal option, I don't think it will significantly impact the table's performance.
If the reason is that now we have two caches that are maintained simultaneously (allRules and pagedRules in useFindRules), could we change the implementation of updateRules so that it updates both of them?
Yes, we have already discussed it on yesterday's sync. A more intelligent cache update algorithm would be a better option. But given the timeframe before FF, there's no chance I can implement it in this PR.
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.
Synced on that with @xcrzx in more detail, the agreement is that in general, it makes sense to update the cache instead of invalidating it (when we enable/disable a rule, update it, etc), but
- we never had it in the implementation of the table previously for most of the rule actions, except the enable/disable action where we have optimistic updates
- it's too late to change that in this PR
Also, the agreement is that updating the rules cache will go as a follow-up performance optimization:
- Implement optimistic updates instead of cache invalidation for rule activation/deactivation
- Implement optimistic updates instead of cache invalidation for bulk operations (need support on the API side)
@@ -56,6 +57,7 @@ export const RuleSwitchComponent = ({ | |||
const [myEnabled, setMyEnabled] = useState(enabled ?? false); | |||
const [, dispatchToaster] = useStateToaster(); | |||
const rulesTableContext = useRulesTableContextOptional(); | |||
const invalidateRules = useInvalidateRules(); |
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.
If we keep the invalidation logic, I'd suggest exposing invalidateRules
from rulesTableContext
to make the usage easier.
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.
No, invalidateRules
should work in places where the rules table context is not available, as the rule editing page.
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.
Synced on that with @xcrzx in more detail.
Pros for keeping useInvalidateRules
and useFindRules
separate from the table context (and thus exposed to other components):
- React query provider is registered at the very top level of the Security Solution's components tree, which makes the data shareable between pages. Rules table context, on the other hand, is registered at the rules page level. The idea is to decouple the "data" itself (the rules cache) from the table state so that the data could be reused in other places. Currently, we don't have any other views that would read rules from the cache, but in theory, this would be possible. In this case, the other view would have its own react context and wouldn't depend on the rules table context.
- Organising data caches hierarchically using cache keys (e.g. like in this implementation, by defining a common "prefix":
[FIND_RULES_QUERY_KEY, 'all']
and[FIND_RULES_QUERY_KEY, 'paged', filterOptions, sortingOptions, pagination]
) is a practice recommended by developers of react-query.
Cons:
- The
paged
rules cache contains table filters, sorting, and pagination options, meaning that it is inherently coupled with the rules table state. This might make its sharing with other views not desirable, depending on the requirements for that view. This is a minor con because the other view could still define its own prefix like[FIND_RULES_QUERY_KEY, 'other']
.
As an action item here that could be addressed in a follow-up PR, useFindRules
, useInvalidateRules
etc could be combined into a single hook.
💚 Build SucceededMetrics [docs]Async chunks
Page load bundle
History
To update your PR or re-run it, just comment with: cc @xcrzx |
Wondering, won't it be confusing for users with no context of how experimental sorting feature works? Should we maybe put some note in description of experimental feature in toggle? |
@vitaliidm could you please open a bug ticket for your observation regarding server-side sorting?
We could add this bug to a "known issues" section in the release notes. |
I think this is fine. And to be honest, the way the backend sorting works right now looks like a bug to me. So I'll create a ticket to see if we can fix that. |
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 haven't tested the PR, but code changes LGTM 👍
Thank you for the quick change of the feature switch implementation!
Summary
Addresses https://github.com/elastic/security-team/issues/1972#issuecomment-1021832714
Changed experimental rules table sorting capabilities feature flag.
Before
A threshold value configured per space (3000 default). Experimental sorting was on by default.
After
A boolean flag configured per user (persisted in the local storage). Experimental sorting is now off by default.
Follow-up: