kibana/x-pack/test/tsconfig.json
Milton Hultgren fa23a90d80
🌊 Refactor API control flow for stream management (#211696)
### Background
This PR is a proposal for a different way to structure the Streams code
flow based on some challenges faced while working on
https://github.com/elastic/streams-program/issues/26 and discussed
[here](https://github.com/elastic/streams-program/discussions/147) and
[here](https://github.com/elastic/streams-program/discussions/55),
mainly around finding it difficult to decide where to place certain
validations that need access to the state as a whole.
It is also in response to some expressed difficulty about how to add new
stream types into the code base.

It aims to achieve 3 goals:
1. It is easy to add new stream types and there is a clear place where
changes (new validation, new logic) for existing stream types happen,
making the code easier to evolve over time
2. It is easier to improve the robustness of the system because there
are clear phases where problems can be caught, fixed and rolled back
3. It lays some ground work for features such as [bulk
changes](https://github.com/elastic/streams-program/issues/125), [dry
runs](https://github.com/elastic/streams-program/discussions/138) and a
[health
endpoint](https://github.com/elastic/streams-program/discussions/139)

In the future, this will most likely be handled by Elasticsearch to a
large degree, as imagined in
https://github.com/elastic/streams-program/discussions/30

The solution takes inspiration from the reconciliation / controller
pattern that Kubernetes uses, where users specify a desired state and
the system takes action towards reaching that step. But it is also
somewhat more similar to how React's Virtual DOM works in that it
happens in a single iteration.

Another key pattern is the [Active Record
pattern](https://www.martinfowler.com/eaaCatalog/activeRecord.html), we
let each stream class contain all the logic for how to validate and
modify that stream in Elasticsearch. The client and `State` class simply
orchestrate the flow but defer all actual work and decision making to
the stream classes.

**Note:** This PoC ignores the management of assets 

### Summary

The process takes the following steps:
1. A route accepts a request (upsert / delete) and translates it into
one or more (for bulk) `StreamChange` objects before passing these to
`State.applyChanges` method (which also takes a toggle for dry runs)
2. The current state of Streams is loaded by using the `State` class
3. The changes are then applied to the current state to derive the
desired state [1]
4. The desired state is then validated, this is done by asking each
individual stream if given the desired state and starting state, from
the perspective of that individual stream, is it in a valid state
(upserted or deleted correctly)
5. If the state is invalid, we return those errors and stop
6. Else we continue, if it's a dry run, we ask the desired state object
for what has changed and report that in the shape of the Elasticsearch
actions that would be attempted
7. Else we proceed to commit the changes to Elasticsearch by asking each
changed stream to determine which Elasticsearch actions need to be
performed to reach the desired state
8. These actions are then combined and sent to the `ExecutionPlan` class
which does planning (mainly for actions around Unwired streams) and then
handles executing the actions in the most parallel way but in the safe
order
9. If any error happens, we attempt to revert back to the starting state
by taking the changed streams and marking each stream as created based
on the starting state and then getting the Elasticsearch actions for
that and applying those

This PR also changes our `resync` endpoint to make use of the same rough
strategy (load current state, mark all as created, get Elasticsearch
actions and apply).

[1] Applying changes:

1. The current state is first cloned
2. Then for each change we see if it is a deletion or an upsert
3. Based on this we either mark existing streams for deletion or
create/update existing streams
10. When creating a new stream instance we use the helper
`streamFromDefinition` which is the only mapping between the definition
documents and the Active Record-style stream type classes
11. As part of this, each stream that changes is marked in the desired
state
12. The stream is passed the desired and current state and should update
itself based on the change
13. The stream can return a set of cascading changes (taking the same
format as the requested changes) which are executed directly after but
we have a limit for how many rounds of cascading changes can happen to
avoid infinite loops

### Adding new stream types

Key in all of this is that the client and `State` classes don't know
anything about any of the specific stream types, they know only of the
`StreamActiveRecord` interface.
When adding a new stream type you need to implement this interface and
update `streamFromDefinition` to create the right class for your new
definition. Streams of different types should only interact with each
other by creating cascading changes.

### Possible follow up tasks
- Introduce a lazy Elasticsearch cluster state cache because multiple
places in the code access the same stuff over and over again
- Make API endpoints the consume `attemptChanges` pass back the
`DesiredState` and planned `ElasticsearchActions` as debug information
based on a flag (maybe also all cascading changes)
- Don't run cascading changes by default but run them if _some_ flag is
submitted based on
https://github.com/elastic/streams-program/discussions/230
- Wrap `attemptChanges` and `resync` with the new LockManager
https://github.com/elastic/kibana/pull/216397
- Unit test WiredStream, UnwiredStream and GroupStream
- Clean up old sync helpers 
- Wrap ES calls to get better stack traces for errors

### Out of scope

- Asset linking and content pack installation (it's probably okay for
these to continue to use the asset client directly since there is less
domain logic and no cascading changes involved)

---------

Co-authored-by: Joe Reuter <johannes.reuter@elastic.co>
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
2025-04-08 13:02:56 +02:00

196 lines
6.3 KiB
JSON

{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "target/types",
"types": ["node", "@kbn/ambient-ftr-types"],
// there is still a decent amount of JS in this plugin and we are taking
// advantage of the fact that TS doesn't know the types of that code and
// gives us `any`. Once that code is converted to .ts we can remove this
// and allow TS to infer types from any JS file imported.
"allowJs": false
},
"include": [
"**/*",
"./api_integration/apis/logstash/pipeline/fixtures/*.json",
"./api_integration/apis/logstash/pipelines/fixtures/*.json",
"./api_integration/apis/telemetry/fixtures/*.json",
"./monitoring_api_integration/fixtures/**/*.json",
"../../typings/**/*",
"../../src/platform/packages/shared/kbn-test/types/ftr_globals/**/*"
],
"exclude": [
"security_solution_cypress/cypress/**/*",
"target/**/*",
"*/plugins/**/*",
"*/packages/**/*",
"*/*/packages/**/*",
"security_solution_api_integration/**/*",
"security_solution_endpoint/**/*"
],
"kbn_references": [
"@kbn/test-suites-src",
"@kbn/core",
"@kbn/data-plugin",
"@kbn/kibana-usage-collection-plugin",
"@kbn/share-plugin",
"@kbn/telemetry-collection-manager-plugin",
"@kbn/telemetry-plugin",
"@kbn/actions-plugin",
"@kbn/alerting-plugin",
"@kbn/apm-plugin",
"@kbn/cases-plugin",
"@kbn/fleet-plugin",
"@kbn/global-search-plugin",
"@kbn/features-plugin",
"@kbn/encrypted-saved-objects-plugin",
"@kbn/event-log-plugin",
"@kbn/features-plugin",
"@kbn/global-search-plugin",
"@kbn/index-management-plugin",
"@kbn/infra-plugin",
"@kbn/licensing-plugin",
"@kbn/ml-plugin",
"@kbn/monitoring-plugin",
"@kbn/observability-plugin",
"@kbn/security-plugin",
"@kbn/security-solution-plugin",
"@kbn/snapshot-restore-plugin",
"@kbn/spaces-plugin",
"@kbn/task-manager-plugin",
"@kbn/telemetry-collection-xpack-plugin",
"@kbn/transform-plugin",
"@kbn/triggers-actions-ui-plugin",
"@kbn/upgrade-assistant-plugin",
"@kbn/remote-clusters-plugin",
"@kbn/cross-cluster-replication-plugin",
"@kbn/synthetics-plugin",
"@kbn/global-search-test-plugin",
"@kbn/test",
"@kbn/repo-info",
"@kbn/tooling-log",
"@kbn/dev-utils",
"@kbn/dev-proc-runner",
"@kbn/ftr-common-functional-services",
"@kbn/securitysolution-list-constants",
"@kbn/expect",
"@kbn/dev-cli-errors",
"@kbn/ci-stats-reporter",
"@kbn/std",
"@kbn/apm-synthtrace",
"@kbn/core-saved-objects-base-server-internal",
"@kbn/rule-data-utils",
"@kbn/maps-plugin",
"@kbn/test-subj-selector",
"@kbn/rison",
"@kbn/reporting-plugin",
"@kbn/aiops-plugin",
"@kbn/ml-agg-utils",
"@kbn/logging",
"@kbn/utility-types",
"@kbn/data-views-plugin",
"@kbn/datemath",
"@kbn/safer-lodash-set",
"@kbn/es-archiver",
"@kbn/config-schema",
"@kbn/es-query",
"@kbn/session-view-plugin",
"@kbn/ml-is-populated-object",
"@kbn/ml-string-hash",
"@kbn/data-visualizer-plugin",
"@kbn/visualizations-plugin",
"@kbn/rule-registry-plugin",
"@kbn/controls-plugin",
"@kbn/core-saved-objects-server",
"@kbn/core-provider-plugin",
"@kbn/user-profile-components",
"@kbn/apm-synthtrace-client",
"@kbn/utils",
"@kbn/journeys",
"@kbn/alerting-api-integration-helpers",
"@kbn/cloud-security-posture-plugin",
"@kbn/cloud-integration-saml-provider-plugin",
"@kbn/security-api-integration-helpers",
"@kbn/alerts-as-data-utils",
"@kbn/discover-plugin",
"@kbn/files-plugin",
"@kbn/shared-ux-file-types",
"@kbn/guided-onboarding-plugin",
"@kbn/field-formats-plugin",
"@kbn/ml-anomaly-utils",
"@kbn/ml-data-frame-analytics-utils",
"@kbn/data-forge",
"@kbn/observability-shared-plugin",
"@kbn/maps-vector-tile-utils",
"@kbn/server-route-repository",
"@kbn/core-http-common",
"@kbn/lens-plugin",
"@kbn/logs-shared-plugin",
"@kbn/telemetry-tools",
"@kbn/profiling-plugin",
"@kbn/observability-onboarding-plugin",
"@kbn/uptime-plugin",
"@kbn/ml-category-validator",
"@kbn/observability-ai-assistant-plugin",
"@kbn/stack-connectors-plugin",
"@kbn/stack-alerts-plugin",
"@kbn/profiling-utils",
"@kbn/profiling-data-access-plugin",
"@kbn/es",
"@kbn/metrics-data-access-plugin",
"@kbn/dataset-quality-plugin",
"@kbn/reporting-export-types-csv-common",
"@kbn/reporting-export-types-pdf-common",
"@kbn/reporting-export-types-png-common",
"@kbn/reporting-common",
"@kbn/io-ts-utils",
"@kbn/security-plugin-types-common",
"@kbn/slo-schema",
"@kbn/typed-react-router-config",
"@kbn/ftr-common-functional-ui-services",
"@kbn/infra-forge",
"@kbn/slo-plugin",
"@kbn/aiops-test-utils",
"@kbn/observability-ai-assistant-app-plugin",
"@kbn/aiops-log-rate-analysis",
"@kbn/apm-data-view",
"@kbn/core-saved-objects-api-server",
"@kbn/search-types",
"@kbn/alerting-comparators",
"@kbn/alerting-state-types",
"@kbn/reporting-server",
"@kbn/data-quality-plugin",
"@kbn/observability-synthetics-test-data",
"@kbn/openapi-common",
"@kbn/securitysolution-lists-common",
"@kbn/securitysolution-exceptions-common",
"@kbn/securitysolution-endpoint-exceptions-common",
"@kbn/entityManager-plugin",
"@kbn/osquery-plugin",
"@kbn/entities-schema",
"@kbn/actions-simulators-plugin",
"@kbn/cases-api-integration-test-plugin",
"@kbn/security-solution-plugin/public/management/cypress",
"@kbn/management-settings-ids",
"@kbn/mock-idp-utils",
"@kbn/cloud-security-posture-common",
"@kbn/saved-objects-management-plugin",
"@kbn/alerting-types",
"@kbn/ai-assistant-common",
"@kbn/core-deprecations-common",
"@kbn/usage-collection-plugin",
"@kbn/palettes",
"@kbn/sse-utils-server",
"@kbn/gen-ai-functional-testing",
"@kbn/automatic-import-plugin",
"@kbn/core-elasticsearch-server",
"@kbn/streams-schema",
"@kbn/server-route-repository-utils",
"@kbn/streams-plugin",
"@kbn/response-ops-rule-params",
"@kbn/scout-info",
"@kbn/inference-common",
"@kbn/apm-sources-access-plugin",
"@kbn/aiops-change-point-detection",
"@kbn/es-errors",
]
}