Skip to content
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

[Fleet] Support Elasticsearch output performance presets #172359

Merged
merged 38 commits into from Dec 6, 2023

Conversation

kpollich
Copy link
Member

@kpollich kpollich commented Dec 1, 2023

Summary

Closes #166870
Closes #172525

  • Adds a new preset field to output saved objects
  • Updates REST spec payloads to allow preset field in POST/PUT requests to the /api/fleet/outputs endpoint
  • Adds logic to set default preset to balanced or custom based on whether a reserved key exists in output.config_yaml
  • Adds UI to the output settings flyout for providing a preset
  • Adds backfill logic to Fleet setup that updates all existing outputs + redeploys their associated policies to ensure the proper preset is provided on all policies

To do

How to test

  1. Create a new Elasticsearch output
  2. Observe the Performance preset dropdown defaults to balanced
  3. Add a performance setting to the custom YAML box e.g. bulk_max_size: 1000
  4. Note the callout with the list of reserved keys
  5. Note that the dropdown switches to Custom and is now disabled
  6. Remove the offending key
  7. Note the dropdown returns to its normal state
  8. Save the output
  9. Edit the output and observe the same behaviors

For the backfill

  1. Create a local environment with multiple elasticsearch outputs on main
  2. Stop Kibana
  3. Checkout this PR branch
  4. Restart Kibana
  5. Observe the ES outputs have been updated to include the appropriate preset value

Screenshots + Screen recordings

Screen.Recording.2023-12-01.at.8.35.59.AM.mov

@kpollich kpollich added the Team:Fleet Team label for Observability Data Collection Fleet team label Dec 1, 2023
@kpollich kpollich self-assigned this Dec 1, 2023
@apmmachine
Copy link
Contributor

🤖 GitHub comments

Expand to view the GitHub comments

Just comment with:

  • /oblt-deploy : Deploy a Kibana instance using the Observability test environments.
  • /oblt-deploy-serverless : Deploy a serverless Kibana instance using the Observability test environments.
  • run elasticsearch-ci/docs : Re-trigger the docs validation. (use unformatted text in the comment!)

@juliaElastic
Copy link
Contributor

juliaElastic commented Dec 4, 2023

I tested that the preset field is added to the agent policy in .fleet-policies when the output is added to the policy. The preset field is also present when I check in agent diagnostics, computed-config.yaml, so that looks good.

Tested that the API populates the preset as expected: balanced if custom_yaml is not provided, custom if provided.

// response has preset: balanced
POST kbn:/api/fleet/outputs
{"name":"local2","type":"elasticsearch","hosts":["http://localhost:9200"],"is_default":false,"is_default_monitoring":false,"config_yaml":"","proxy_id":null}

// response has preset: custom
POST kbn:/api/fleet/outputs
{"name":"local2","type":"elasticsearch","hosts":["http://localhost:9200"],"is_default":false,"is_default_monitoring":false,"config_yaml":"queue.mem.events: 3650","proxy_id":null}

When I edit an output with preset:balanced, and I add a value in the custom yaml box, should we change to preset:custom automatically? Or at least show a callout that values will be ignored unless the user sets preset:custom.
image

@kpollich
Copy link
Member Author

kpollich commented Dec 4, 2023

When I edit an output with preset:balanced, and I add a value in the custom yaml box, should we change to preset:custom automatically? Or at least show a callout that values will be ignored unless the user sets preset:custom.

Yes we should show the callout.

One thing to note is that we're only doing a rudimentary string match on reserved keys, not parsing the YAML and checking the resulting key contents. So there will be some non-obvious behavior for now, e.g.

# Successfully forces `custom` preset, display callout
queue.mem.events: 1000

# Doesn't force the preset or show the callout, but would still be ignored by agent
queue:
  mem:
    events: 1000

I think this is an okay corner to cut for now, but if we have time it would be good to get a more sophisticated parsing pattern in place where we

  1. Parse the YAML with safeLoad
  2. Check the resulting objects' keys for a given dot-separated path

This might not be too hard with some combination of the js-yaml library's defaults and some lodash magic, so I'll give it a shot after tests are written and CI is happier. If it's taking a while, though, I think we could punt to an enhancement.

@kpollich
Copy link
Member Author

kpollich commented Dec 4, 2023

Created follow up issues for docs + better YAML parsing:

#172523

#172525

@kpollich kpollich marked this pull request as ready for review December 4, 2023 21:11
@kpollich kpollich requested a review from a team as a code owner December 4, 2023 21:11
@kpollich kpollich enabled auto-merge (squash) December 5, 2023 16:50
@kpollich
Copy link
Member Author

kpollich commented Dec 5, 2023

One thing to note is that we're only doing a rudimentary string match on reserved keys, not parsing the YAML and checking the resulting key contents. So there will be some non-obvious behavior for now, e.g.

Successfully forces custom preset, display callout

queue.mem.events: 1000

Doesn't force the preset or show the callout, but would still be ignored by agent

queue:
mem:
events: 1000

The second format here is the one most commonly used and the one used in our queue documentation in Beats (technically it is a slightly different third format): https://www.elastic.co/guide/en/beats/filebeat/current/configuring-internal-queue.html

queue.mem:
  events: 4096
  flush.min_events: 512
  flush.timeout: 5s

I think this is going to cause some confusion. If we can fix this we should. Beats and Agent YAML has supported this format for a long time so I'd expect it's the style most people would write even if we were to update our reference examples.

It doesn't have to be fixed in this PR, but we should avoid having this gap live for very long.

@cmacknz I have added actual YML parsing to the validation here + unit tests. Hence, this will now close #172525.

Comment on lines 45 to 59
export function outputYmlIncludesReservedPerformanceKey(configYml: string) {
if (!configYml || configYml === '') {
return false;
}

const parsedYml = yaml.safeLoad(configYml);

if (!isObject(parsedYml)) {
return RESERVED_CONFIG_YML_KEYS.some((key) => parsedYml.includes(key));
}

const flattenedYml = isObject(parsedYml) ? getFlattenedObject(parsedYml) : {};

return RESERVED_CONFIG_YML_KEYS.some((key) => Object.keys(flattenedYml).includes(key));
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function now parses YML if possible when checking for reserved keys

Comment on lines 52 to 102
describe('outputYmlIncludesReservedPerformanceKey', () => {
describe('dot notation', () => {
it('returns true when reserved key is present', () => {
const configYml = `queue.mem.events: 1000`;

expect(outputYmlIncludesReservedPerformanceKey(configYml)).toBe(true);
});

it('returns false when no reserved key is present', () => {
const configYml = `some.random.key: 1000`;

expect(outputYmlIncludesReservedPerformanceKey(configYml)).toBe(false);
});
});

describe('object notation', () => {
it('returns true when reserved key is present', () => {
const configYml = `
queue:
mem:
events: 1000
`;

expect(outputYmlIncludesReservedPerformanceKey(configYml)).toBe(true);
});

it('returns false when no reserved key is present', () => {
const configYml = `
some:
random:
key: 1000
`;

expect(outputYmlIncludesReservedPerformanceKey(configYml)).toBe(false);
});
});

describe('plain string', () => {
it('returns false when no reserved key is present', () => {
const configYml = `bulk_max_size`;

expect(outputYmlIncludesReservedPerformanceKey(configYml)).toBe(true);
});

it('returns true when reserved key is present', () => {
const configYml = `just a string`;

expect(outputYmlIncludesReservedPerformanceKey(configYml)).toBe(false);
});
});
});
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Relevant YML parsing test cases here - we also still check plain strings even if the YML is invalid just to be sure.

@kpollich kpollich enabled auto-merge (squash) December 5, 2023 19:06
@kpollich
Copy link
Member Author

kpollich commented Dec 5, 2023

@elasticmachine merge upstream

@kpollich
Copy link
Member Author

kpollich commented Dec 6, 2023

Bundle size blew up because of the js-yaml import in client code. Pushed 08f3d12 to lazy load the output flyout (probably something we could do for every flyout or portal, though most of them wouldn't make a large difference in bundle size)

@kpollich
Copy link
Member Author

kpollich commented Dec 6, 2023

@elasticmachine merge upstream

@kpollich
Copy link
Member Author

kpollich commented Dec 6, 2023

js-yaml is accounting for the massive increase in bundle size here, and doesn't support tree-shaking: nodeca/js-yaml#661. Going to see if I can come up with an alternative.

@kpollich
Copy link
Member Author

kpollich commented Dec 6, 2023

Hmm maybe my interpretation of the webpack analyzer is wrong or I'm missing something. We import js-yaml plenty of other places in our React code so I don't see how it would have blown up the bundle size like this, e.g. we use the same `import { safeLoad } from 'js-yaml' module import in: https://github.com/elastic/kibana/blob/main/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/page_steps/add_integration.tsx

@kibana-ci
Copy link
Collaborator

💚 Build Succeeded

Metrics [docs]

Async chunks

Total size of all lazy-loaded chunks that will be downloaded as the user navigates the app

id before after diff
fleet 1.2MB 1.2MB +1.9KB

Page load bundle

Size of the bundles that are downloaded on every page load. Target size is below 100kb

id before after diff
fleet 156.0KB 156.7KB +755.0B
Unknown metric groups

ESLint disabled line counts

id before after diff
fleet 45 44 -1

Total ESLint disabled count

id before after diff
fleet 56 55 -1

History

To update your PR or re-run it, just comment with:
@elasticmachine merge upstream

cc @kpollich

@kpollich kpollich merged commit 6fe6cdd into elastic:main Dec 6, 2023
38 checks passed
@kibanamachine kibanamachine added the backport:skip This commit does not require backporting label Dec 6, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport:skip This commit does not require backporting release_note:enhancement Team:Fleet Team label for Observability Data Collection Fleet team v8.12.0
Projects
None yet