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

[Uptime] Improve monitor charts query #30561

Merged

Conversation

justinkambic
Copy link
Contributor

@justinkambic justinkambic commented Feb 8, 2019

Summary

Resolves #29843.

This change would update the query used for fetching monitor chart data, and clean up computations done on the client that probably belong in the server.

Testing this PR

The changes enacted will have visual results, so aside from providing code review you should be able to see it's working as intended.

  • Start up Heartbeat
  • Select a monitor
  • Verify that the duration value in the status bar looks correct. Things to watch for:
    • Make sure the value isn't really fast. We had a bug recently that caused the duration value to be "converted" twice, meaning val / (1000 * 1000). A request to https://www.elastic.co is not going to complete in 0.15ms.
    • Likewise, make sure the value doesn't look too large, i.e. not being converted at all. A request to https://www.elastic.co should not take 150,000ms either.
  • Verify the duration stats in the duration chart seem like they're on an appropriate scale. I've included a sample screenshot below:
    image

This PR is in progress, and shouldn't be reviewed until #30441 is merged.

@justinkambic justinkambic added bug Fixes for quality problems that affect the customer experience WIP Work in progress v7.0.0 refactoring Team:Uptime - DEPRECATED Synthetics & RUM sub-team of Application Observability v6.7.0 labels Feb 8, 2019
@justinkambic justinkambic self-assigned this Feb 8, 2019
@elasticmachine
Copy link
Contributor

Pinging @elastic/uptime

@elasticmachine
Copy link
Contributor

💔 Build Failed

@andrewvc
Copy link
Contributor

andrewvc commented Feb 8, 2019

I know this is in progress, but I'd like to add a piece of feedback. The query changes are great.

Is there a concrete need for the reformatting to be moved to the server? The chart takes a weird format. That responsibility seems more tightly coupled to the Eui widget than anything else. From a performance standpoint where that transformation happens seems negligible.

If we do that formatting on the server that doesn't have much of an impact today due to the GQL API being private. However, if we decide to create a public GQL API in the future that makes things awkward. Generally speaking APIs are cleaner if they are more agnostic of consumer details.

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

@justinkambic
Copy link
Contributor Author

If we do that formatting on the server that doesn't have much of an impact today due to the GQL API being private. However, if we decide to create a public GQL API in the future that makes things awkward. Generally speaking APIs are cleaner if they are more agnostic of consumer details.

Yeah that makes sense, we should remove that bit from this change.

@justinkambic justinkambic force-pushed the uptime_improve-monitor-charts-query branch from 37faba6 to 6eea265 Compare February 12, 2019 14:41
@elasticmachine
Copy link
Contributor

💚 Build Succeeded

@justinkambic
Copy link
Contributor Author

@andrewvc if you check out 7b0bbe4, it should address your concerns about unit conversion.

@justinkambic justinkambic added review and removed WIP Work in progress labels Feb 12, 2019
description="The 'ms' is an abbreviation for 'milliseconds'."
/>
</EuiFlexItem>
{!!duration && (
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think we're ok to just omit this element if there's no duration for some reason. There should always be one though.

Copy link
Contributor

Choose a reason for hiding this comment

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

++

@elasticmachine
Copy link
Contributor

💔 Build Failed

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

Copy link
Contributor

@andrewvc andrewvc left a comment

Choose a reason for hiding this comment

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

Functionality is great, but I think the code could use some tweaks. See included comments :)

"The timeseries value for this point in time."
x: UnsignedInteger!
"The min duration value in microseconds at this time."
y0: Float
Copy link
Contributor

Choose a reason for hiding this comment

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

I realllly dislike the y0, y distinction, but I'm OK with it here since this is a private API.

If we ever make it public I'd like it to be yMin,yMax or similar. I'll leave it to you whether you feel it should be changed for this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh ok - I don't have any objection to changing the name. I don't like that convention either.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@andrewvc if you take a look at e4ea5b5, let me know what you think.

}

"Represents the average monitor duration ms at a point in time."
type MonitorDurationAveragePoint {
Copy link
Contributor

Choose a reason for hiding this comment

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

I feel like at some later point we'll have more generic CartesianPoint types, but I'm fine with this for now.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tend to think of these chart-related bits of code as in a state of flux that will be overwritten/deleted (because we're going to need to re-implement the charts when new EUI components are available). We will want to take care that we approach it with nuance and sensibility when we overwrite what we have today.

const mappedBuckets = buckets.map(bucket => {
const x = get(bucket, 'key');
const docCount = get(bucket, 'doc_count', 0);
statusMaxCount = Math.max(docCount, statusMaxCount);
Copy link
Contributor

Choose a reason for hiding this comment

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

This needs comments for clarity, it's pretty confusing what's going on here. A comment saying "This can be used to calculate the maximum Y bounds for a chart" would be quite helpful.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added some comments in 3ccdd1d, please elaborate if you still think it's unclear.

durationMaxValue,
statusMaxCount,
};
return mappedBuckets.reduce(
Copy link
Contributor

Choose a reason for hiding this comment

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

Is map/reduce getting us much here?

As a fan of FP (and former lisper) I like MR, but it seems to be interfering with code clarity here. We have an entire intermediate set of variables created in the map phase that only exists to be discarded and renamed in the reduce phase.

This would be more clear to read using a simple forEach that mutated the counter object. Something like the untested code below:

const result: MonitorChart = {
    durationArea: [],
    durationLine: [],
    status: [],
    durationMaxValue: 0,
    statusMaxCount: 0,
};

buckets.forEach(bucket => {
  const x = get(bucket, 'key');
  const docCount = get(bucket, 'doc_count', 0);
  result.statusMaxCount = Math.max(docCount, result.statusMaxCount);
  result.durationMaxValue = Math.max(result.durationMaxValue, get(bucket, 'duration.max', 0));
  
  result.durationArea.push({ x, y0: get(bucket, 'duration.min', null), y:  get(bucket, 'duration.max', null)});
  result.durationLine.push({ x, y: get(bucket, 'duration.avg', null) });
  result.status.push(formatStatusBuckets(x, get(bucket, 'status.buckets', []), docCount));
});
return result;

Alternatively, if you did want to use map reduce, I'd skip the map phase completely. The goal here is to turn a list (of buckets) into a single value. That corresponds to a reduce. The code as it stands uses a map phase to expose an intermediate state. However, that intermediate state is, I would argue, confusing, because it doesn't do much other than rename variables. You could do something similar to the forEach above, but use reduce instead. Like (also untested):

const result: MonitorChart = {
    durationArea: [],
    durationLine: [],
    status: [],
    durationMaxValue: 0,
    statusMaxCount: 0,
};

return buckets.reduce( (bucket, acc) => {
  const x = get(bucket, 'key');
  const docCount = get(bucket, 'doc_count', 0);
  result.statusMaxCount = Math.max(docCount, result.statusMaxCount);
  result.durationMaxValue = Math.max(result.durationMaxValue, get(bucket, 'duration.max', 0));
  
  result.durationArea.push({ x, y0: get(bucket, 'duration.min', null), y:  get(bucket, 'duration.max', null)});
  result.durationLine.push({ x, y: get(bucket, 'duration.avg', null) });
  result.status.push(formatStatusBuckets(x, get(bucket, 'status.buckets', []), docCount));
  return result;
}, result);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

First of all - thank you for taking the time to write all this out.

Secondly I agree with your points. Sometimes we are focused on the code's output so much we forget that others will need to read it in the future, and we implement the very first solution that comes to mind. I think your first solution is best. If we need to declare an accumulator variable before the functions no matter what, it makes sense to access it from the simplest possible block, which in this case would be forEach.

Please review 3ccdd1d when you have a chance.

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

Copy link
Contributor

@andrewvc andrewvc left a comment

Choose a reason for hiding this comment

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

LGTM. Looking good!

@justinkambic
Copy link
Contributor Author

jenkins test this

@justinkambic
Copy link
Contributor Author

@andrewvc I'm going to re-test just because these changes were last built two weeks ago. If CI passes we'll be good to merge IMO.

@elasticmachine
Copy link
Contributor

💔 Build Failed

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

@justinkambic justinkambic merged commit 729fdfd into elastic:master Mar 1, 2019
justinkambic added a commit to justinkambic/kibana that referenced this pull request Mar 1, 2019
* Refactor chart querying.

* Fix monitor chart query.

* Refactor several inline computations to helper functions. Improve schema naming.

* Move unit conversion to client, remove bare conversion values.

* Add API tests for monitor charts.

* Add test for conversion function.

* Add type annotations to latest schema additions.

* Fix typo.

* Refactor based on PR feedback, add comments asked for in PR feedback.

* Rename fields in schema, update tests. Extract monitor charts to functional component and add unit test.
justinkambic added a commit to justinkambic/kibana that referenced this pull request Mar 5, 2019
* Refactor chart querying.

* Fix monitor chart query.

* Refactor several inline computations to helper functions. Improve schema naming.

* Move unit conversion to client, remove bare conversion values.

* Add API tests for monitor charts.

* Add test for conversion function.

* Add type annotations to latest schema additions.

* Fix typo.

* Refactor based on PR feedback, add comments asked for in PR feedback.

* Rename fields in schema, update tests. Extract monitor charts to functional component and add unit test.
justinkambic added a commit that referenced this pull request Mar 5, 2019
* Refactor chart querying.

* Fix monitor chart query.

* Refactor several inline computations to helper functions. Improve schema naming.

* Move unit conversion to client, remove bare conversion values.

* Add API tests for monitor charts.

* Add test for conversion function.

* Add type annotations to latest schema additions.

* Fix typo.

* Refactor based on PR feedback, add comments asked for in PR feedback.

* Rename fields in schema, update tests. Extract monitor charts to functional component and add unit test.
justinkambic added a commit that referenced this pull request Mar 5, 2019
* Refactor chart querying.

* Fix monitor chart query.

* Refactor several inline computations to helper functions. Improve schema naming.

* Move unit conversion to client, remove bare conversion values.

* Add API tests for monitor charts.

* Add test for conversion function.

* Add type annotations to latest schema additions.

* Fix typo.

* Refactor based on PR feedback, add comments asked for in PR feedback.

* Rename fields in schema, update tests. Extract monitor charts to functional component and add unit test.
@justinkambic
Copy link
Contributor Author

@justinkambic justinkambic deleted the uptime_improve-monitor-charts-query branch March 5, 2019 19:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Fixes for quality problems that affect the customer experience refactoring review Team:Uptime - DEPRECATED Synthetics & RUM sub-team of Application Observability v7.0.0 v7.2.0 v8.0.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants