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

DATAUP-176 slow startup #1840

Merged
merged 13 commits into from
Oct 26, 2020
Merged

DATAUP-176 slow startup #1840

merged 13 commits into from
Oct 26, 2020

Conversation

briehl
Copy link
Member

@briehl briehl commented Oct 5, 2020

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.

  • Please include a summary of the change and which issue is fixed.
  • Please also include relevant motivation and context.
  • List any dependencies that are required for this change.

Jira Ticket / Issue

e.g. https://kbase-jira.atlassian.net/browse/DATAUP-X

  • Added the Jira Ticket to the title of the PR e.g. (DATAUP-69 Adds a PR template)

Testing Instructions

  • Details for how to test the PR:
  • Tests pass in travis and locally
  • Changes available by spinning up a local narrative and navigating to X to see Y

Dev Checklist:

  • My code follows the guidelines at https://sites.google.com/truss.works/kbasetruss/development
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules
  • I have run Black and Flake8 on changed Python Code manually or with git precommit (and the travis build passes)

Updating Version and Release Notes (if applicable)

@coveralls
Copy link

coveralls commented Oct 5, 2020

Coverage Status

Coverage increased (+0.8%) to 17.167% when pulling a37e97a on DATAUP-176-slow-startup into 4953ad1 on develop.

@briehl
Copy link
Member Author

briehl commented Oct 6, 2020

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.

@briehl briehl marked this pull request as ready for review October 7, 2020 23:05
// 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) {
Copy link
Member Author

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.

Copy link
Member Author

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.

Copy link
Collaborator

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', () => {
Copy link
Member Author

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', () => {
Copy link
Member Author

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.

Copy link
Member Author

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) {
Copy link
Member Author

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);
Copy link
Member Author

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 = {
Copy link
Member Author

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 () => {
Copy link
Member Author

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');
Copy link
Member Author

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 = () => {
Copy link
Member Author

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);
});

Copy link
Member Author

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!

eamahanna
eamahanna previously approved these changes Oct 8, 2020
Copy link
Contributor

@eamahanna eamahanna left a 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 eamahanna dismissed their stale review October 12, 2020 23:33

Tests are failing

@briehl
Copy link
Member Author

briehl commented Oct 20, 2020

@eamahanna Tests are passing now. I'm not sure why they failed before. :P


it('Should return a boolean for is_loaded', () => {
const narr = new Narrative();
expect(narr.isLoaded()).toEqual(DEFAULT_FULLY_LOADED); // default as set in
Copy link
Collaborator

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?

Copy link
Member Author

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.)

Comment on lines +150 to +154
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);
Copy link
Collaborator

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.

Copy link
Member Author

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.

Comment on lines 174 to 178
} else {
return new Workspace(Config.url('workspace'), {
token: this.getAuthToken(),
})
.get_workspace_info({ id: this.workspaceId })
Copy link
Collaborator

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.

Comment on lines 191 to 192
})
.get_workspace_info({ id: this.workspaceId })
Copy link
Collaborator

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?

Copy link
Member Author

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);
Copy link
Collaborator

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

Copy link
Collaborator

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?

Copy link
Member Author

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.

Comment on lines 320 to 325
$([Jupyter.events]).on(
'delete.Cell',
function () {
// this.enableKeyboardManager();
}.bind(this)
);
Copy link
Collaborator

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="' +
Copy link
Collaborator

@ialarmedalien ialarmedalien Oct 23, 2020

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?

Copy link
Collaborator

@ialarmedalien ialarmedalien left a 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.

@briehl
Copy link
Member Author

briehl commented Oct 23, 2020

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.

@sonarcloud
Copy link

sonarcloud bot commented Oct 23, 2020

Kudos, SonarCloud Quality Gate passed!

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities (and Security Hotspot 0 Security Hotspots to review)
Code Smell A 0 Code Smells

No Coverage information No Coverage information
0.0% 0.0% Duplication

@briehl
Copy link
Member Author

briehl commented Oct 23, 2020

Once tests pass, I think I can merge. That might not be until Monday, though, with Travis's backlog...

@briehl
Copy link
Member Author

briehl commented Oct 23, 2020

Noting here that this is going against develop, which should be periodically merged into truss anyway.

@briehl briehl merged commit ad31ace into develop Oct 26, 2020
@briehl briehl deleted the DATAUP-176-slow-startup branch October 26, 2020 17:15
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