-
Notifications
You must be signed in to change notification settings - Fork 33
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
Conversation
I have to fix something. I've just realised that unfortunately all grid graphs are awkwardly landing in one container. |
Graphs landing in one container problem was solved by more uniquely ID distribution (as the D3 is working on the |
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.
great job
import Grid from './Grid/Grid'; | ||
|
||
function composeID(q) { | ||
const modFuncArity = q.split(':'); |
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.
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
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.
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
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'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, |
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.
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)
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.
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
tomonitoredCollection
, - rename the
mfa
tomonitored
.
What do you think?
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.
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) |
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.
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
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.
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') { |
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.
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> |
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.
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'; |
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.
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 = { |
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.
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 = { |
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.
For keeping application's state we are using Redux, not internal compoment's state
graphID: PropTypes.number.isRequired, | ||
}; | ||
|
||
export default Grid; |
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.
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
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 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
?
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.
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) { |
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.
Move it to utils
|
Ok, so all tests are passed. Additionally for the sake of explanation, the omnipresent |
Great work!! |
import GridGraph from '../GridGraph'; | ||
import LineGraph from '../LineGraph'; | ||
import { GRAPH_TYPE } from '../../../constants/GraphTypes'; | ||
import { GRAPH_INITIAL_SIZE } from '../../../constants'; |
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.
import { GRAPH_INITIAL_SIZE, GRAPH_TYPE } from '../../../constants';
setSize, | ||
size, | ||
}) => { | ||
const monitoredID = safecomposeID(monitored.query); |
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.
Move monitoredID
to the state. Dispatch an action in getMonitoredFunctions
which will create/delete IDs.
Added new visualisation - heatmap (Grid). Also the @gomoripeti
backend_graph_type
branch is integrated in that PR (changes pasted inside appropriate files). TheGrid
component was very large in size so in the end it is broken into two pieces: a graph itself and helper functions (both are insideGrid
folder).This solves #107.