[DataViews] - Add caching to fields.toSpec#224767
[DataViews] - Add caching to fields.toSpec#224767michaelolo24 wants to merge 3 commits intoelastic:mainfrom
Conversation
90659fd to
c50000e
Compare
| expect(shortDotsList[0].spec.shortDotsEnable).toBe(true); | ||
| }); | ||
|
|
||
| it('does not fail if add is called with a field with the same name', () => { |
There was a problem hiding this comment.
added, but not sure if this should be valid?
There was a problem hiding this comment.
Hmm hard to say, but I'd be hesitant to change it now 😅
| this.byName.delete(field.name); | ||
|
|
||
| const fieldIndex = findIndex(this, { name: field.name }); | ||
| if (fieldIndex === -1) return; |
There was a problem hiding this comment.
if a name is provided not actually in the list, the mutation still took place.
There was a problem hiding this comment.
Wait, so did this used to just always remove the last field in the list if passed a not-found field? 🤔
|
Pinging @elastic/security-threat-hunting-investigations (Team:Threat Hunting:Investigations) |
7c52bdc to
47eccad
Compare
💔 Build Failed
Failed CI Steps
Test Failures
Metrics [docs]Page load bundle
History
|
davismcphee
left a comment
There was a problem hiding this comment.
The code changes look good in general and the idea makes sense, but it's hard to have confidence in these types of changes since data views are so broadly relied on. I left a few comments and questions, and some ideas on how we might better gauge the impact. It's going to be a tricky one for sure.
Also what approach were you using for testing the changes locally? I'm not really sure where to begin with it tbh.
| const firstSpec = indexPattern.fields.toSpec(); | ||
| const secondSpec = indexPattern.fields.toSpec(); | ||
| expect(firstSpec).toEqual(secondSpec); |
There was a problem hiding this comment.
One risk here is if there's anywhere in the codebase that retrieves and modifies a spec, it could corrupt the cache value. Maybe we should use defaultFreeze on it like what's done on responses in this PR, which will freeze it in dev and help catch these issues.
Do you know if CI runs in dev? I'd feel a bit more confident if we had a full CI run with freezing enabled in case it reveals any issues. Otherwise we might want to let it go through CI once with deepFreeze explicitly to see if it passes.
There was a problem hiding this comment.
Nice to actually have some tests for this now 👍
| it('does not throw if remove is called on a field not in the list', () => { | ||
| const field = new DataViewField({ ...baseField, name: 'notfound' }); | ||
| expect(() => list.remove(field)).not.toThrow(); | ||
| }); | ||
|
|
||
| it('does not throw if update is called on a field not in the list', () => { | ||
| const field: FieldSpec = { ...baseField, name: 'notfound' }; | ||
| expect(() => list.update(field)).not.toThrow(); | ||
| }); |
There was a problem hiding this comment.
Are these duplicates of the above tests with "handles _ on non-existent field gracefully"?
| expect(shortDotsList[0].spec.shortDotsEnable).toBe(true); | ||
| }); | ||
|
|
||
| it('does not fail if add is called with a field with the same name', () => { |
There was a problem hiding this comment.
Hmm hard to say, but I'd be hesitant to change it now 😅
| existingField.runtimeField = undefined; | ||
| this.fields.clearSpecCache(); |
There was a problem hiding this comment.
It sucks that we need to expose a public clearSpecCache method just for this case... But it also sucks that we do this by mutating the field (and that we expose mutable props on the field class in the first place). Kinda makes me feel like we should try sticking readonly on all the public field class props and run a CI build to see if we catch anywhere else in the codebase where we do this, since it could corrupt the cache value.
Thinking about it a bit more, wouldn't we need to invalidate the entire field list cache whenever any property of any field is modified? Most of them look to be mutable.
| this.byName.delete(field.name); | ||
|
|
||
| const fieldIndex = findIndex(this, { name: field.name }); | ||
| if (fieldIndex === -1) return; |
There was a problem hiding this comment.
Wait, so did this used to just always remove the last field in the list if passed a not-found field? 🤔
There was a problem hiding this comment.
A couple of questions to better understand the security use case:
- Why does security use data view specs so heavily? Would it not work better to use actual data view instances instead, which are already cached by the data views service?
- Would using specs that don't include the fields, e.g.
toSpec(false)ortoMinimalSpec()help at all here? I'm guessing not since presumably you want to keep the fields around?
|
Closing this for now as we do not need to rely on this for our performance enhancements, but a noticeable benefit was seen for even Discover with this caching layer. |
Summary
The goal of this PR is primarily for performance improvements. When the index patterns in a given dataview have a significant enough number of fields, the performance of the application can slow down pretty significantly. When accessing the dataview saved object directly, we have the benefit of the network request being cached here.
This drastically helps with the performance when working with the dataview directly, but when working with the
DataViewSpecproduced byDataView.toSpec()here, there is no such optimization. This can be problematic given a significant number of fields and the nested iterations in thefields.toSpec()call seen here and here.This PR introduces a simple in memory cache for this potentially expensive
fields.toSpec()call.Checklist
Check the PR satisfies following conditions.
Reviewers should verify this PR satisfies this list as well.
Identify risks