-
Notifications
You must be signed in to change notification settings - Fork 8.2k
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
Instrument vis_type_vislib, lens and vis_type_timeseries with execution context service #105206
Conversation
@@ -146,6 +153,7 @@ describe('configureClient', () => { | |||
"200 | |||
GET /foo?hello=dolly | |||
{\\"seq_no_primary_term\\":true,\\"query\\":{\\"term\\":{\\"user\\":\\"kimchy\\"}}}", | |||
undefined, |
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 decided to update the test instead of refactoring.
? { | ||
http: { request: { id: event.meta.request.options.opaqueId } }, | ||
} | ||
: undefined; // do not clutter logs if opaqueId is not present |
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.
"request": { }
adds noise to the logs and consumes more disk space, but doesn't bring any value.
const opaqueId = event.meta.request.options.opaqueId; | ||
const meta = opaqueId | ||
? { | ||
http: { request: { id: event.meta.request.options.opaqueId } }, |
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.
we will remove this logic when we start including trace.id
in the logs. #102699
Instead, trace.id
will be used for the log correlation. But right now we need it for testing.
src/core/types/execution_context.ts
Outdated
export interface KibanaExecutionContext { | ||
// to make it compatible with SerializableState | ||
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions | ||
export type KibanaExecutionContext = { |
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 used to get is incompatible with index signature
error when declared it as interface
.
We cannot use KibanaExecutionContext extends SerializableState
since Core cannot depend on the Kibana plugins.
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 this is the relevant typescript issue: microsoft/TypeScript#15300
@@ -58,6 +62,7 @@ export function getFunctionDefinition({ | |||
timeFields: args.timeFields, | |||
timeRange: get(input, 'timeRange', undefined), | |||
getNow, | |||
executionContext: executionContext?.toJSON(), |
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.
The data
plugin data fetching model is a bit different from the one most plugins use. It doesn't send an HTTP request for every search operation, but batches them according to some internal rules and send them in bulk. In turn, the Kibana server parses the batch and issues a dedicated search request for every search operation. Kibana server streams Elasticsearch server response back to the browser as soon as every search operation is finished.
So I had to refactor the plugin a bit to support executionContext
propagation.
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 are we passing json here ?
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.
the downstream code requires SerializableState
, so I had to convert to json
before calling fetch$
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.
SerializableState is any object that can be serialized, seems KibanaExecutionContext already is
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.
KibanaExecutionContext
is serializable, but the handler
receives https://github.com/elastic/kibana/pull/105206/files#diff-357bbbe4bfbe5a09c1e453b99baba2082a43ef48d34d1677831cc3c485f1bca6R70
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 there a way to make it serializable without converting to json 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.
is there a way to make it serializable without converting to json string ?
It's not a json string, it returns a json object. Actually, any object with toJSON method is serializable with JSON.stringify, but SerializableState
interface doesn't support it.
executionContext: this.deps.start().core.executionContext.create({ | ||
type: this.vis.params.type, | ||
name: this.vis.type.name, | ||
id: this.vis.id ?? 'unknown_id', |
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.
In what cases it may be empty? What should we use instead?
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.
unsaved visualization
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.
embeddable shouldn't be creating the execution context (as else we don't know if request came from dashboard or visualize for example) it should be the app that is responsible for this.
we should add execution context to EmbeddableInput (just as we did with searchSessionId)
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.
as discussed in person, we will add this later. see Nested context
in #102629
name: this.savedVis.visualizationType ?? '', | ||
description: this.savedVis.title ?? this.savedVis.description ?? '', | ||
id: this.id, | ||
url: this.output.editUrl, |
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 saw url: "app/lens#/edit_by_value"
during the testing. What url should I use instead?
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.
that's probably correct url, other state is passed thru navigateToAppwithstate
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.
For the "by-value" Lens panels we can't provide a linkable URL yet. Is there a way we could link to the dashboard URL instead, as the dashboard is has a URL?
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 there a way we could link to the dashboard URL instead, as the dashboard is has a URL?
not yet. in #102626 I'm going to add nested context support, so we can create and link dashboard context
and visualization context
.
Pinging @elastic/kibana-core (Team:Core) |
ignore my comment about using search sessions for this i don't think its gonna be helpful |
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.
Tested according to your instructions and found that it was working well. My main comments are about the actual values that you've chosen for Lens & Visualize executionContext.
| [id](./kibana-plugin-core-server.kibanaexecutioncontext.id.md) | <code>string</code> | unique value to indentify find the source | | ||
| [name](./kibana-plugin-core-server.kibanaexecutioncontext.name.md) | <code>string</code> | public name of a user-facing feature | | ||
| [type](./kibana-plugin-core-server.kibanaexecutioncontext.type.md) | <code>string</code> | Kibana application initated an operation. Can be narrowed to an enum later. | | ||
| [url](./kibana-plugin-core-server.kibanaexecutioncontext.url.md) | <code>string</code> | in browser - url to navigate to a current page, on server - endpoint path, for task: task SO url | |
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.
@mshustov It looks like the generated docs have lost some comments- can you bring them back?
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.
Hm..it's how our doc generator handles interface
conversion into type
. Let me see what I can do with it.
type: this.vis.params.type, | ||
name: this.vis.type.name, |
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.
Both of these values are the same in practice, is that intentional? Seems like if they are going to be the same value they should use the same accessor.
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 was going to use type: visualization
, it might be useful for cases like lens
or actions
when we need an additional sub-type to identify a source.
type: this.vis.params.type, | ||
name: this.vis.type.name, | ||
id: this.vis.id ?? 'unknown_id', | ||
description: this.vis.title ?? this.vis.type.title, |
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.
Maybe the title should be used as name
and this.vis.description
should be used as the description?
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.
name
is documented as the public name of a user-facing feature
so I think providing the name of the type of visualization better maps to that field. description
is better suited for the specific instance of the feature, like the specific visualization title.
@@ -323,7 +325,15 @@ export class Embeddable | |||
if (this.input.onLoad) { | |||
this.input.onLoad(true); | |||
} | |||
const executionContext = this.deps.executionContext.create({ | |||
type: this.savedVis.type ?? 'lens', | |||
name: this.savedVis.visualizationType ?? '', |
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 you want to track it as name: 'lnsPie'
for example? It's not a unique name.
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.
@wylieconlon Yes, id
and description
might be unique, but name
is a public-facing name of the Kibana entity.
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.
Seems like maybe we could make this parameter name more explicit in a follow-up. Maybe this would be more clear:
type
renamed tofeature
name
renamed tofeatureSubtype
description
renamed toinstanceDescription
id
renamed toinstanceId
@mshustov WDYT?
name: this.savedVis.visualizationType ?? '', | ||
description: this.savedVis.title ?? this.savedVis.description ?? '', | ||
id: this.id, | ||
url: this.output.editUrl, |
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.
For the "by-value" Lens panels we can't provide a linkable URL yet. Is there a way we could link to the dashboard URL instead, as the dashboard is has a URL?
|
||
export type { MountPoint, UnmountCallback, PublicUiSettingsParams } from './types'; | ||
|
||
export { URL_MAX_LENGTH } from './core_app'; | ||
|
||
export type { KibanaExecutionContext } from './execution_context'; |
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 the additional type export from the same location needed?
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.
src/core/server/execution_context/execution_context_container.test.ts
Outdated
Show resolved
Hide resolved
type: this.vis.params.type, | ||
name: this.vis.type.name, | ||
id: this.vis.id ?? 'unknown_id', | ||
description: this.vis.title ?? this.vis.type.title, |
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.
name
is documented as the public name of a user-facing feature
so I think providing the name of the type of visualization better maps to that field. description
is better suited for the specific instance of the feature, like the specific visualization title.
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.
code LGTM
💚 Build SucceededMetrics [docs]Public APIs missing comments
Async chunks
Page load bundle
Unknown metric groupsAPI count
History
To update your PR or re-run it, just comment with: cc @joshdover |
…on context service (elastic#105206) Co-authored-by: Josh Dover <1813008+joshdover@users.noreply.github.com>
💚 Backport successful
This backport PR will be merged automatically after passing CI. |
Summary
Part of #101587
It integrates a few visualizations with a mechanism allowing users to understand what Kibana entity initiated a search request.
It can be used to identify a source of slowness via Elasticsearch slow log query: the slow log record contains
x-opaque-id
header, which value is propagated from the Kibana server or browser App.The current PR integrates the
execution_context
service with :kibana/src/plugins/vis_type_vislib/public/types.ts
Lines 39 to 50 in cc41571
I'd appreciate any suggestion from the team-owners on improving the logic or general guidance on the
execution_context
service integration.Implementation
The browser environment doesn't provide an API to keep track of the "thread" context during async operations.
In nodejs env, we rely on AsyncLocalStorage and Async hooks for this purpose.
Thus, for the browser env, we have to implement an alternative approach. A Kibana plugin creates an
async context object
and passes it manually through the whole invocation chain to a place when the Kibana browser app communicates with the Kibana server via an HTTP protocol. Then the propagatedasync context object
is serialized and passed through the network to the Kibana server. In turn, the Kibana server stores it as a part of the "thread" context, emitsasync context object
to the logs, and attaches id and type ofasync context object
as part ofx-opaque-id
header to every call to the Elasticsearch server. Therefore, a Kibana user can identify what a feature initiated a request via Kibana logs (underelasticsearch.query
logger) or Elasticsearch logs (x-opaque-id
is attached tosearch slow logs
).How to test
Unfortunately, right at this moment, only manually. I'm going to add integration tests in a follow-up PR.
Steps to test the PR manually:
request.id: opaqueId
inelasticsearch response
andexecution_context
whenever it's setSample eCommerce orders
.