-
Notifications
You must be signed in to change notification settings - Fork 8.1k
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
[EBT] Add Elastic V3 shippers #130162
[EBT] Add Elastic V3 shippers #130162
Conversation
e3dd9e8
to
386bd7c
Compare
81434a1
to
3fcbf42
Compare
noParse: [ | ||
/[\/\\]node_modules[\/\\]lodash[\/\\]index\.js$/, | ||
/[\/\\]node_modules[\/\\]vega[\/\\]build[\/\\]vega\.js$/, | ||
|
||
// Ignore @elastic/analytics server-side shipper implementation: | ||
/[\/\\]node_modules[\/\\]@elastic[\/\\]analytics[\/\\]target_node[\/\\]shippers[\/\\]elastic_v3[\/\\]server_shipper[\/\\]index\.js$/, |
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 server-side shipper implementation being loaded into a front-end bundle? I feel like that's the real problem, not that it's being parsed by webpack.
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 we might need to split this package since it seems to have some server-specific code and some ui-specific code. We should probably have @kbn/analytics-client
and @kbn/analytics-server
or something. Common code could go into @kbn/analytics-common
.
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.
FWIW, the client is the same for both, the UI and the Server. The difference is the implementation of the transport layer that ships the events to our Remote Telemetry Service.
There is a piece of logic deciding which implementation to import and use depending on the environment:
if (isBrowser()) {
import('./ui_shipper');
} else {
import('./server_shipper');
}
So, essentially, the server-side will never be used on the UI and should not be included in the bundle. Please, bear in mind that other libraries do similar stuff: https://github.com/segmentio/analytics-next/blob/master/src/core/connection/index.ts <- we are considering Segment as a potential candidate for shipper and they offer the same library for both: UI and Server side.
I wish Webpack were able to identify this and only import the parts of the code that are used. Otherwise, we are bundling entire libraries like lodash
or rxjs
only to use a small subset of methods. That seems a huge cost in terms of bundle size.
We can split the package in 3 to have common
, ui
and server
. And it'll play nice for our package and monorepo approach in Kibana. However, given that this package will eventually make it to its own repo and NPM module, it feels like we'd be setting ourselves, the Platform Analytics team as the future maintainers of this library, and any other Elastic products using this client to a larger dependency tree. I wonder if we can do anything at the Bazel level to not expose everything at the root level so shippers can be imported on demand by the app without requiring the entire library? This could reduce core
bundle size as it doesn't care about the shippers, as well as reducing cloud
and telemetry
plugins as they'd only import the shipper they are configuring (fullstory
and elastic_v3
respectively).
Exposing mocks
from the same library is another use case for this that we now need to overcome by creating a separate package.
A potential different approach is to create a package for the client and a different one for each shipper. However, these are intended to be Elastic-approved shippers that should be used when using the client. Having to install a separate library for each one of them feels like making them "optional".
I hope anything in my rambling makes sense. Please, let me know what's the best approach here, bearing in mind that it'll be eventually moved to a separate repo.
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 understand the idea, and it's something we've considered a lot in the design of the packaging system. The primary issue with supporting something like this is that it makes the build process for packages require a lot more customization which makes it more fragile and harder to reason about.
Right now every package implements it's own build process with a custom BUILD.bazel file and they are primarily written and maintained by Operations. Once we incorporate all code in the repo into package we can't have hundreds of bespoke build processes in use. Instead we've settled on a base set of build procedures which can be reused across packages. Supporting some level of nesting in these build procedures is technically possible, but complicated, and we've decided that it's not worth implementing because the simpler approach has an additional benefit: it helps discourage dependency bottlenecks in the system.
Dependency bottlenecks are like lodash
, rxjs
, or the data
plugin where a single package is created to contain a bunch of separate things and consumers regularly depend on that package for a small fraction of it's API. These bottlenecks lead to circular dependencies, unnecessary rebuilds and test runs, cache thrashing, and many other issues. Their primary benefit is that there are less packages overall, but they have caused so many problems for us over the years and we're trying to get rid of them going forward. In the package system we're building we want to encourage folks to instead break those packages up into pieces which means more packages, enforcing the need to make it trivial to create packages and to standardize on the build procedures.
Maybe once we get past this initial rollout we can look at making more complicated package types, but for now we see a lot of simplicity in this approach and think the trade-offs are worth the added cost of having more packages. Please try the package generator:
node scripts/generate package --help
Creating new packages shouldn't be hard and will get even easier soon.
I hope this helps explain why we think this is the right way forward, if @kbn/analytics
moves out of the Kibana repo then an entire build procedure for that new repo will have to be defined and I think it will be a perfect time to revisit the idea of using separate packages. I'd also be happy to help with ESLint rules at that time which automatically rewrites all the @kbn/analytics-client
and @kbn/analytics-server
imports.
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.
Makes sense! Thanks for explaining!
node scripts/generate package --help
I actually used it to generate the @elastic/analytics
package (only that I manually renamed it to @elastic/
instead of `@kbn/' because of the already mentioned intention to eventually move it outside Kibana. 😇 It worked like a charm 😍
I'll see how I can do to minimize the amount of code duplication while keeping a clear split between UI and shippers. I think the best approach can be:
- Keep
@elastic/analytics
to hold the main client and types. This package is imported bycore
only. - Create
@elastic/analytics-shipper-fullstory
that imports the types only from@elastic/analytics
and implements the FullStory shipper. This package is used by thecloud
plugin. - Create
@elastic/analytics-shipper-elastic_v3-common
to hold the common logic and helpers for the "elastic V3" shipper. - Create
@elastic/analytics-shipper-elastic_v3-ui
that imports the*-common
and includes the ui-side implementation of the shipper. Imported by thetelemetry
plugin in thepublic
directory. - Create
@elastic/analytics-shipper-elastic_v3-server
that imports the*-common
and includes the server-side implementation of the shipper. Imported by thetelemetry
plugin in theserver
directory.
With this split, we might not need to import add the package to the shared libs: #130027
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.
Nice! Sounds like you've got a good handle on how you want to lay this out and being able to avoid adding things to shared libs sounds like a win!
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 looks good so far. Although I'd love to see a plugin functional test (inside test/plugin_functional/plugins
) that actually ships data to different shippers and we test that the pipeline is working.
sendTo === 'production' | ||
? 'https://telemetry.elastic.co' | ||
: 'https://telemetry-staging.elastic.co'; | ||
return `${baseUrl}/v3/send/${channelName}`; |
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 v3 endpoint accepts a ?debug=true
parameter which returns useful information such as bucket path. I wonder if we should accommodate adding this from the start?
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.
It's added as a query parameter instead of the URL builder :)
💚 Build SucceededMetrics [docs]Module Count
Public APIs missing comments
Async chunks
Public APIs missing exports
Page load bundle
Unknown metric groupsAPI count
ESLint disabled line counts
Total ESLint disabled count
History
To update your PR or re-run it, just comment with: |
I'll move this back to draft until I resolve the split into packages. |
PR to split the package: #130574 I'll rebase this one once it's merged. |
Closing this PR in favour of #130696 |
Summary
Implements the Elastic V3 shippers for both, the UI (resolves #125691) and the server (resolves #125693).
I noticed that the server implementation is loaded in the bundle even when it's not used, so I added a line in our
webpack.config.ts
file to stop parsing the server implementation.The shippers needed a
shutdown
mechanism to flush the queues so I had to add theshutdown
methods to the shippers, analytics client andstop
tocore.analytics
service. I preferredshutdown
overstop
in the package because the verb feels more destructive (you couldstart
afterstop
). Let me know if I should change this tostop
instead.This PR also registers the V3 shippers in the
telemetry
plugin (both,public
andserver
sides).Checklist
For maintainers