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

Adding heatmap and graph_type #134

Merged
merged 25 commits into from
Jun 12, 2018
Merged

Adding heatmap and graph_type #134

merged 25 commits into from
Jun 12, 2018

Conversation

jlampar
Copy link
Contributor

@jlampar jlampar commented May 8, 2018

Added new visualisation - heatmap (Grid). Also the @gomoripeti backend_graph_type branch is integrated in that PR (changes pasted inside appropriate files). The Grid component was very large in size so in the end it is broken into two pieces: a graph itself and helper functions (both are inside Grid folder).
This solves #107.

@jlampar
Copy link
Contributor Author

jlampar commented May 9, 2018

I have to fix something. I've just realised that unfortunately all grid graphs are awkwardly landing in one container.

@jlampar
Copy link
Contributor Author

jlampar commented May 9, 2018

Graphs landing in one container problem was solved by more uniquely ID distribution (as the D3 is working on the document level, not on the component level, it went crazy seeing multiple, same ID's).
Post Scriptum: I consider this version as a temporary one although fully functional. I can try to exchange the cycle of redrawing the graph to cycle of recolouring it (probably a lot less resource-consuming). Our previous C3 graph was in fact redrawing itself - I tried to create something in a similar fashion. I can try archiving this now - in this PR - or some time later. What do you think?

Copy link
Collaborator

@gomoripeti gomoripeti left a comment

Choose a reason for hiding this comment

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

great job

import Grid from './Grid/Grid';

function composeID(q) {
const modFuncArity = q.split(':');
Copy link
Collaborator

Choose a reason for hiding this comment

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

you shouldn't assume anything about the m.query it can have different format depending on Erlang or Elixir mode (eg Mod.fun/arity in case of Elixir) and it can have more advanced formats than just mod-fun-arity (eg #funcount mfa = mod:fun(1, _) )
I would be better to use m.mfa to compose the ID

Copy link
Collaborator

Choose a reason for hiding this comment

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

there used to be a utility function chartId which we stopped using, but it can be reintroduced for safe IDs https://github.com/Appliscale/xprof/blob/master/priv/app/utils.js#L2

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'll use the chartId(MFA) function. If it worked before, it will work fine. Nevertheless I'll reintegrate it and test.


const propTypes = {
dps: PropTypes.arrayOf(PropTypes.object).isRequired,
type: PropTypes.string.isRequired,
query: PropTypes.string.isRequired,
Copy link
Collaborator

@gomoripeti gomoripeti May 9, 2018

Choose a reason for hiding this comment

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

Maybe this can be the whole m json object (including mfa and query fields) to address above issue with composing the ID
(we need to find a good name for the json object, I dont know if function is good enough, in generic sense this is a monitor or monitored-object, which can be an m:f/a but also can be garbage_collection or memory in the future)

Copy link
Contributor Author

@jlampar jlampar May 10, 2018

Choose a reason for hiding this comment

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

If we are composing the ID inside GraphPanel, in the end we are only passing the ID to the Graph and the panel has access to the whole m object. The panel needs the object as it decomposes it into the grid props, whereas the grid just receives stuff from the panel.
In case of renaming the JSON object I think this is a good idea, as for now the object named mfa has also a mfa property which is a little bit confusing (that's why I renamed it somewhere to m in loops or arguments).
I propose we:

  • rename the mfas to monitoredCollection,
  • rename the mfa to monitored.

What do you think?

Copy link
Collaborator

Choose a reason for hiding this comment

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

right, so query is only there so that the Graph can show it as a title. +1 for renamings

@@ -29,11 +29,13 @@ export const getMonitoredFunctions = () => async (dispatch, getState) => {
if (error) {
console.log('ERROR: ', error);
} else if (!isEqual(mfas, json)) {
const newMfas = json.filter(mfa => !mfas.map(f => f[3]).includes(mfa[3]));
const newMfas = json
.filter(mfa => !mfas.map(f => f.query)
Copy link

Choose a reason for hiding this comment

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

nitpicking: you could perform and collect mfas.map(f => f.query) before filtering to do it only once instead of doing it for each mfa from incomming json

Copy link
Contributor Author

@jlampar jlampar May 10, 2018

Choose a reason for hiding this comment

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

Now it is:

const queries = mfas.map(f => f.query);
const newMfas = json
  .filter(mfa => !queries
    .includes(mfa.query));

(for the sake of clarity, given in old, already changed naming convention)

const queryID = composeID(query);
const wrapperID = `graphWrapper-${queryID}`;
const wrapper = document.getElementById(wrapperID);
if (type === 'grid') {
Copy link
Contributor

Choose a reason for hiding this comment

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

We can create constant for types and put it somewhere in src/constants

export const GRAPH_TYPES = {
  GRID: 'grid',
  LINE: 'line',
}

outerWidth={wrapper.clientWidth}
graphID={passId}
/>}
</div>
Copy link
Contributor

Choose a reason for hiding this comment

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

As we introduced new type of graph we can add new layer of components here

  switch (type) {
    case GRAPH_TYPES.GRID:
      return (<GridGraph />);
    case GRAPH_TYPES.LINE:
      return (<LineGraph />);
    default:
      return
  }
     

@@ -0,0 +1,416 @@
import React from 'react';
Copy link
Contributor

Choose a reason for hiding this comment

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

To keep convention please move Grid/ folder to monitoring/ (one level higher):
apps/xprof_gui/priv/src/components/monitoring/Grid/Grid.jsx

src 
  components
    components in container1
       component1
       component2 
        ...
  containers
    container1
    container2 
    ...

class Grid extends React.Component {
constructor(props) {
super(props);
this.state = {
Copy link
Contributor

Choose a reason for hiding this comment

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

For keeping application's state we are using Redux, not internal compoment's state. We should treat component as pure functions

@@ -26,10 +38,31 @@ const propTypes = {
};

class Monitoring extends React.Component {
constructor(props) {
super(props);
this.state = {
Copy link
Contributor

Choose a reason for hiding this comment

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

For keeping application's state we are using Redux, not internal compoment's state

graphID: PropTypes.number.isRequired,
};

export default Grid;
Copy link
Contributor

Choose a reason for hiding this comment

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

A lot is happening in this component, we should keep component's code short and readable. My proposal is to move all state-related logic into actions and whole d3-related logic to src/utils

Copy link
Contributor Author

@jlampar jlampar May 23, 2018

Choose a reason for hiding this comment

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

I have to give a brief update considering this. Currently I'm working on making the component more flexible and thus I've also cleared this mess, adding three, D3-heavy methods to the class: compose(props), update() and reCompose(props) which are evoked from the lifecycle methods. Without much explanation it looks like this:

  componentWillMount() {
    window.addEventListener('resize', this.updateWindowDimensions);
    this.updateWindowDimensions();
    prepareTooltip();
  }

  componentDidMount() {
    this.compose(this.props);
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps !== this.props) {
      this.updateWindowDimensions();
      this.reCompose(nextProps);
    }
  }

  componentWillUpdate() {
    this.update();
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.updateWindowDimensions);
  }

[and then go all the methods... ]

What is the best way? Should I keep those composing functions as methods of the Grid component or split it between actions and src/utils?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Or maybe the better way is to move all D3-logic to the gridUtils and have only one place where the D3 is imported?

@@ -3,6 +3,14 @@ import PropTypes from 'prop-types';
import { GraphPanel } from '../';
import { DATA_INTERVAL } from '../../../constants';

function roll(func) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Move it to utils

@jlampar
Copy link
Contributor Author

jlampar commented Jun 5, 2018

  1. GRAPH_TYPE constants were added and placed in the constants folder.
  2. New layer of components was added with switch taking the GRAPH_TYPE constants into consideration - resolving whether it is line graph or grid graph.
  3. The Grid folder was moved up. The new folders for the graph components were placed accordingly.
  4. Components state is now managed by Redux and passed through props. No internal components state.
  5. The gargantuan Grid component was reduced in complexity. The D3 logic resides now in two files:
  • gridComposingUtils.js where all the compose and recompose code is stored (the majority of D3 SVG creation and recreation flow)
  • gridUtils.js where all the small helper functions are.
  1. The tests were a little bit fixed to fit the new form of Erlang output (object instead of an array).

@jlampar
Copy link
Contributor Author

jlampar commented Jun 8, 2018

Ok, so all tests are passed. Additionally for the sake of explanation, the omnipresent GRAPH_INITIAL_SIZE constant was added because of strange logs I've got all the way. All the time it was logging that the size prop was undefined although it is required (but it came from the Redux state in which it has a defined initial state - it somehow ignored it). And the size prop is constitutional for the grid to be created.

@MarcinKasprowicz
Copy link
Contributor

Great work!! :shipit: :shipit: :shipit:

import GridGraph from '../GridGraph';
import LineGraph from '../LineGraph';
import { GRAPH_TYPE } from '../../../constants/GraphTypes';
import { GRAPH_INITIAL_SIZE } from '../../../constants';
Copy link
Contributor

Choose a reason for hiding this comment

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

import { GRAPH_INITIAL_SIZE, GRAPH_TYPE } from '../../../constants';

setSize,
size,
}) => {
const monitoredID = safecomposeID(monitored.query);
Copy link
Contributor

Choose a reason for hiding this comment

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

Move monitoredID to the state. Dispatch an action in getMonitoredFunctions which will create/delete IDs.

@gomoripeti gomoripeti merged commit 4db675d into release_2.0 Jun 12, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants