-
Notifications
You must be signed in to change notification settings - Fork 53
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
DATAUP-176 slow startup #1840
DATAUP-176 slow startup #1840
Conversation
This PR will also probably lower the coverage a little bit, since some JS files were missing from that calculation. Sigh. Will get updated in a separate PR. |
// This should not be run until AFTER the notebook has been loaded! | ||
// It depends on elements of the Notebook metadata. | ||
Narrative.prototype.init = function () { | ||
Narrative.prototype.init = function (jobsReadyCallback) { |
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.
This function has most of the main changes that aren't just linting / whitespace shifts.
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.
jobsReadyCallback
was put in to give the test suite something to hang a Promise onto, since the main tasks here are done by callback to DOM / jquery events.
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 should have scrolled down to here and ignored the above! 😖
}; | ||
|
||
$([Jupyter.events]).on('notebook_loaded.Notebook', function () { | ||
$([Jupyter.events]).on('notebook_loaded.Notebook', () => { |
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.
Everything under this event is still untested, and probably warrants a little rewriting. Not the focus of this PR, but will be in the next one.
.then(() => this.narrController.render()) | ||
.finally(() => this.sidePanel.render()); | ||
}); | ||
$([Jupyter.events]).on('kernel_connected.Kernel', () => { |
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.
Here's the primary change - kernel_ready.Kernel
only fires when the kernel is ready for input, and not busy. If there's something running, it'll never fire. This will look like there's a failure, when really the kernel is busily crunching some code.
kernel_connected.Kernel
fires when the connection is set up properly. It should be much more consistent here.
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 also made the event response set up in parallel with the notebook_loaded.Notebook
event above, instead of only set up when the Notebook event fires. I've never seen them not go in that order, but it's possible, and this is cleaner.
.initCommChannel() | ||
.then(() => { | ||
this.loadingWidget.updateProgress('jobs', true); | ||
if (jobsReadyCallback) { |
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.
The callback should be optional - it's mainly used for testing.
// This should not be run until AFTER the notebook has been loaded! | ||
// It depends on elements of the Notebook metadata. | ||
Narrative.prototype.init = function () { | ||
Narrative.prototype.init = function (jobsReadyCallback) { | ||
// NAR-271 - Firefox needs to be told where the top of the page is. :P | ||
window.scrollTo(0, 0); |
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.
This can probably be removed by now, but that can get tested in a proceeding PR.
* When that event is no longer bound (i.e. when the node is removed, OR when .unbind is called) | ||
* it triggers the 'remove' function. Lets us keep track of when widgets get removed | ||
* in the registerWidget function below. | ||
*/ | ||
$.event.special.destroyed = { |
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 don't think this ever gets invoked, and can probably be removed as well. A later PR.
describe('Test the kbaseNarrative module', () => { | ||
let loginDiv = $('<div>'); | ||
|
||
beforeEach(async () => { |
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.
Testing was challenging.
beforeEach mocks up the /token and /me calls to auth - these define the auth session, which gets consumed by Narrative.init. So they're kinda necessary. The mock returns a truncated object with enough info to get through.
It also mocks a "Jupyter" object. We aren't testing the Jupyter machinery, but need some of it - specifically the kernel - to be present. Each of those elements are either called by the Narrative.init object, or the jobCommChannel. Mocking the comm channel is probably a better solution, but due to the nature of AMD load orders and testing, that's not trivial. This is verbose, but much easier to control with few side effects.
|
||
it('Should instantiate', () => { | ||
const narr = new Narrative(); | ||
expect(narr.maxNarrativeSize).toBe('10 MB'); |
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.
Just using this magic value to make sure there's a Narrative object present.
|
||
it('init should fail as expected when the job connection fails', () => { | ||
return new Promise((resolve, reject) => { | ||
Jupyter.notebook.kernel.comm_info = () => { |
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.
tweak the mocked kernel to always throw an error on connection.
const narr = new Narrative(); | ||
expect(narr.getAuthToken()).toBe(FAKE_TOKEN); | ||
}); | ||
|
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's a zillion more tests for that module, but I wanted to keep the code in this PR more manageable. More upcoming!
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.
LGTM. This looks like a beast to test. Thanks for tackling it and including notes to help reviewers understand what was happening.
@eamahanna Tests are passing now. I'm not sure why they failed before. :P |
test/unit/spec/kbaseNarrativeSpec.js
Outdated
|
||
it('Should return a boolean for is_loaded', () => { | ||
const narr = new Narrative(); | ||
expect(narr.isLoaded()).toEqual(DEFAULT_FULLY_LOADED); // default as set in |
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.
// default as set in
unfinished comment?
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 choose to have it be dangling. Where could it be set!? I want the reader to have the same anxiety I feel when I run these tests.
(ok, I'll fix it. Thanks for catching.)
expect(narr.uiModeIs('edit')).toBe(DEFAULT_WRITABLE); | ||
expect(narr.uiModeIs('view')).toBe(!DEFAULT_WRITABLE); | ||
Jupyter.notebook.writable = !Jupyter.notebook.writable; | ||
expect(narr.uiModeIs('edit')).toBe(!DEFAULT_WRITABLE); | ||
expect(narr.uiModeIs('view')).toBe(DEFAULT_WRITABLE); |
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.
Might also be worth checking that Jupyter.notebook.writable
and DEFAULT_WRITABLE
are the same when you first instantiate the new Narrative object, so that you know that the initialisation has worked correctly and that the uiModeIs
function is returning the correct result.
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.
That's a very good idea.
} else { | ||
return new Workspace(Config.url('workspace'), { | ||
token: this.getAuthToken(), | ||
}) | ||
.get_workspace_info({ id: this.workspaceId }) |
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.
can remove a level of indenting here as the if
statement triggers a return.
}) | ||
.get_workspace_info({ id: this.workspaceId }) |
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.
the indent here is a little confusing! presumably that's prettier
at work?
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 agree! That's good ol' eslint trying its best. :) I'll rework to make, er, prettier.
} catch (ex) { | ||
// console.warn('Error removing shortcut "' + shortcut +'"', ex); | ||
// console.warn('Error removing shortcut '' + shortcut +'"', ex); |
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.
this line won't parse correctly if uncommented
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.
This is pre-existing code, I know, but do you know why has this one been commented out but the others left in?
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 just tested, and it's commented out because of how that whole block is structured. It tries to remove those shortcuts from both the command and edit lists, but they're only in one or the other, so there's 30ish cases of warning spam on every page load if that's uncommented.
That whole function needs an overhaul, imo. Next PR.
$([Jupyter.events]).on( | ||
'delete.Cell', | ||
function () { | ||
// this.enableKeyboardManager(); | ||
}.bind(this) | ||
); |
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.
still needed?
@@ -322,35 +349,47 @@ define([ | |||
* after there's a visible DOM element for it to render in. | |||
*/ | |||
Narrative.prototype.initSharePanel = function () { | |||
var sharePanel = $('<div style="text-align:center"><br><br><img src="' + | |||
var sharePanel = $( | |||
'<div style="text-align:center"><br><br><img src="' + |
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 in another PR that inline style and the <br>
s could be removed and replaced by a class with an appropriate amount of padding-top
?
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.
The core of the code looks fine. Just a few comments that you may or may wish to act upon.
Thanks. Yeah, I'll address those. There's a TON of other changes I'd like to make to that module in upcoming PRs (probably in some free time, if I can wrangle some). I'd first like to get much more test coverage, then start peeling out old code that isn't used, and update some old style code to be more in-line with choices we've made recently. |
Kudos, SonarCloud Quality Gate passed! 0 Bugs No Coverage information |
Once tests pass, I think I can merge. That might not be until Monday, though, with Travis's backlog... |
Noting here that this is going against develop, which should be periodically merged into truss anyway. |
Description of PR purpose/changes
This PR addresses some of the events that occur during Narrative startup. The startup process consists of 5 primary steps that are checked off of a tracking div, ending with the disappearance of that div.
There are two of these that are closely dependent on the kernel and what it's doing - these are checkbox 4 and 5 on the list. In the first kernel step, we try to make sure the connection between the FE and the Jupyter notebook kernel exists and is stable and ready for input. This is currently done by waiting for the
kernel_ready.Kernel
event to get fired. This happens when the kernel is connected and idle. This can be a problem if the kernel already exists and is busy, which can happen on a page reload. It's therefore possible for the kernel_ready.Kernel event to not be fired in any reasonable amount of time. It's equally likely that the kernel isn't really usable at that point, and we're best off preventing the user from getting frustrated with that, but at that point there might be other interventions. Either way, this shouldn't block a user from getting to their Narrative.Setting that event to
kernel_connected.Kernel
addresses this by filling the checkbox as soon as the kernel is connected to the FE. It might not be ready for events at that point, but this shouldn't affect the user experience that much, especially if the user is expecting some cell to finish running.This PR also starts the process of cleaning up and testing the kbaseNarrative module. I'm going to try to do that in chunks, since it's a huge module.
Jira Ticket / Issue
e.g. https://kbase-jira.atlassian.net/browse/DATAUP-X
Testing Instructions
Dev Checklist:
Updating Version and Release Notes (if applicable)