-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
Improve Callback Graph #1179
Improve Callback Graph #1179
Conversation
Love where this is going @jjaraalm ! |
Amazing work! I’m happy to see GraphVis go in favour of a more modern alternative that enables rich interactions :) thanks for doing this! |
@chriddyp @nicolaskruchten Thanks! It's a fun project, and I think it could be useful too! |
That profiling screenshot is so good! It would be cool if we could time the callback execution on the backend and pass that time delta up with the |
Agreed, a better breakdown of the various timings would be helpful. Especially since right now, I'm only looking at the delay between request and response. Even simple callbacks (repeatedly clicking a button) can look expensive if they happen often and the queue gets backed up. This shouldn't be too hard to do and I'll look into it. Glad you all are interested! |
Awesome stuff @jjaraalm ! Feel free to ping me if you have any questions 👍 |
(FYI I am assigning myself just to denote to the rest of our team that I'll be your ambassador for this pull request) |
@chriddyp sounds good. I've added support for reporting server-side timings using Users can tag and report timings for resources using a new API like: @app.callback(Output('slow-store', 'data'), [Input('style-output', 'style'), Input('out', 'children')])
def combine_data(style, value):
time.sleep(0.1)
dash.callback_context.record_timing('task_1', 0.1, 'The first task.')
time.sleep(0.7)
dash.callback_context.record_timing('task_2', 0.7)
time.sleep(0.2)
dash.callback_context.record_timing('task_3', 0.2, 'Cleanup task.')
return dict(style=style, value=value) which translates into the HTTP headers:
The Distinguishing between "queue time" and "network time" seems difficult and I'm not quite sure how to do it. The difference between |
Brilliant idea! I was not aware of these headers before now, this is very nice. Re
Yeah good point, this is a tricky one to record but also to communicate to our users. I suppose that there are two queues:
So, when we display In some cases, the queuing time will dominate the time. In most cases, I think the payload size itself will dominate. So, another metric we could display could be the size of the request. We can get this from the
Thinking a bit about this now, this one actually might be pretty tricky. I think the main components that a user might care about is the graph component as will handle the most amount of data on the page. We do asynchronous rendering here, so even if we knew when React "finished" rendering the component, we wouldn't be able to capture the actual rendering time. This might be a rabbit hole, so feel free to stay out of it for now, I think the other performance breakdowns will be the most valuable for our users! Playing around with this locally, another thing that comes to mind is conditionally re-rendering the callback tree based off of which components are visible on the page. So, if you have a large multi-page application, you would only see the callbacks that are associated with outputs or inputs that are currently rendered on the page. This data is available in the We merged & released some deeper changes to the This is looking really great otherwise and I know the community will love it. Are you interested in continuing to add new features to this or would you be looking to try to merge this in soon and follow up with more features? Either way is fine! |
@chriddyp I'm in no rush to get this merged in. I'd love to add more features, but just need to find time to work on it at some point. I didn't realize that Re: #1103, I've been waiting on that so I'm more than happy to accommodate. Interestingly, I noticed that #1103 (or some other change in dev) messes with the |
Yep, that was me in #1103 mucking with the existing callback graph implementation - had to tweak it a bit in order to not totally choke on the dict IDs. Sorry about the merge conflicts! Feel free to file a bug report about the instability you're seeing, but my personal feeling is what you have here is already such an improvement that we should work to get it merged without adding any more features than it already has; then we can add all these other ideas in future PRs. |
I agree! The work done in this PR already improves things 10x, I would love to see the community start to use this new version 👍 |
I'd love to see a screenshot of what this looks like when multiple props are involved in a single component, similar to the |
@jjaraalm Thanks for all your efforts to improve the callback graph implementation -- the enhanced context provided by this PR will be very helpful for debugging purposes. One brief question about response.headers.add("Server-Timing", 'dash_total;dur={}'.format(dash_total["dur"])) |
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.
Seems good except for the linting failure 💃
c.parentElement.removeChild(c); | ||
}); | ||
""" | ||
) |
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.
New feature for dash.testing
- percy_snapshot
adds a kwarg convert_canvases
that turns every <canvas>
into an <img>
with matching contents, then puts the canvases back afterward. This lets us capture Cytoscape, among other things (cc @xhlulu)
Sizing isn't quite as expected, but I assume that's just Percy rendering as a different screen size from what's used in circleci.
name: 'cose', | ||
padding: 10, | ||
animate: false | ||
} |
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.
@nicolaskruchten I couldn't come up with a single layout that always seemed better than the others, so made a dropdown to choose among three. Another side benefit of this is if you drag nodes around to tweak the layout, and you want to get back to the original, you can change to a different algo and then back to the original.
from dash.dependencies import Output, Input | ||
|
||
|
||
def test_dvct001_callback_timing(dash_thread_server): |
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.
@rpkyle this test should cover parity between Python and R for the Server-Timing
header, including record_timing
.
Attempt to improve and significantly expand the features available in the callback graph to improve its usefulness in debugging and testing. Will try to cover the ideas given in #1176. Comments/suggestions would be welcome, especially for styling.
Current Status
Changes
viz.js
withcytoscape.js
viareact-cytoscape
. This results in some minor visual and layout changes.CallbackGraphContainer
now subscribes tolayout
andpaths
so it can introspect the current state.Issues
dagre
was giving horrible results even when cycles were pruned away. Usingbreadthfirst
for now, but layout properties may need to be tailored to viewport size instead of being fixed.no_update
are still being flashed during execution highlighting. No way to detect them right now.Contributor Checklist
optionals
CHANGELOG.md