Skip to content

Startup the jupyter server when appropriate #9708

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

Merged
merged 14 commits into from
Jan 23, 2020
Merged

Conversation

rchiodo
Copy link

@rchiodo rchiodo commented Jan 22, 2020

For #7232

We will now start the jupyter server process (well the daemon in most cases) when:

  • User opens a notebook
  • User opens the interactive window
  • User opens a workspace that activates the extension and the user has done either of the previous two in the last 7 days

On my machine (once the server has started), execution is basically instantaneous.

@rchiodo
Copy link
Author

rchiodo commented Jan 22, 2020

And to be noted, our existing functional tests caught a bug in my implementation. Woot.

@@ -142,6 +152,11 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor {
localize.DataScience.nativeEditorTitle(),
ViewColumn.Active
);

// Start the server as soon as we open if allowed
if (!this.configService.getSettings().datascience.disableJupyterAutoStart) {
Copy link

@DonJayamanne DonJayamanne Jan 22, 2020

Choose a reason for hiding this comment

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

I think auto starting of notebooks should be done outside the editor.
I'd prefer the editor focus on editing alone. #Resolved

Copy link
Author

Choose a reason for hiding this comment

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

The editor has always started the server, just not when it opens.


In reply to: 369669813 [](ancestors = 369669813)

Copy link

@DonJayamanne DonJayamanne Jan 22, 2020

Choose a reason for hiding this comment

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

I think it's a good time to move this out.
At least auto starting. Feels like to much is happening in this space and we keep wrong more stuff. #Resolved

Copy link
Author

Choose a reason for hiding this comment

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

Disagree. The internal implementation of the notebook (interactiveBase actually) requires a server to run code. Where is it supposed to get this server from if it doesn't start it? It assumes it was already started? That sounds like the wrong dependency to me.


In reply to: 369689773 [](ancestors = 369689773)

Copy link
Author

Choose a reason for hiding this comment

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

Maybe the auto starting though. Just not the ensureServer calls.


In reply to: 369692802 [](ancestors = 369692802,369689773)

Copy link
Author

Choose a reason for hiding this comment

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

Actually that breaks the connection between the server and the UI. It doesn't know it got started. So I'll have to add other code to check if it's already started.


In reply to: 369693209 [](ancestors = 369693209,369692802,369689773)

Copy link
Author

Choose a reason for hiding this comment

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

Which means some sort of global event on the JupyterExecution class.


In reply to: 369695742 [](ancestors = 369695742,369693209,369692802,369689773)

await this.fileSystem.createDirectory(path.dirname(filePath));
return this.fileSystem.writeFile(filePath, contents);
} else {
return this.fileSystem.deleteFile(filePath);
Copy link

@DonJayamanne DonJayamanne Jan 22, 2020

Choose a reason for hiding this comment

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

Do we care if there are any exceptions in here when deleting a backup file? #WontFix

Copy link

@DonJayamanne DonJayamanne Jan 22, 2020

Choose a reason for hiding this comment

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

If not, I'd prefer adding that and removing the catch. Feels like catch will bury issues instead of them getting reported. #WontFix

}

private onDidOpenOrCloseInteractive(interactive: IInteractiveWindow | undefined) {
if (interactive) {
Copy link

@DonJayamanne DonJayamanne Jan 22, 2020

Choose a reason for hiding this comment

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

Not sure why this is necessary?
Is it possible to open an interactive window without starting a server?
Also, why should we start a server when switching between tabs from interactive window to something else? #Resolved

Copy link
Author

Choose a reason for hiding this comment

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

There's no reason to start it on shutdown.


In reply to: 369673234 [](ancestors = 369673234)

Copy link
Author

Choose a reason for hiding this comment

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

Maybe I should rename startServer. it doesn't just start a server. It checks if one was succesfully started, and if so, writes the memento item to indicate it was succesful.


In reply to: 369690429 [](ancestors = 369690429,369673234)

// This is the list of things that should cause us to start a local server
// 1) Notebook is opened
// 2) Notebook was opened in the past 7 days
// 3) Interactive window was opened in the past 7 days

Choose a reason for hiding this comment

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

Lets discuss these rules/criteria.

// Make sure it has a server
assert.ok(editor.notebook, 'Notebook did not start with a server');

// Close down all servers and mock restarting
Copy link

@DonJayamanne DonJayamanne Jan 22, 2020

Choose a reason for hiding this comment

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

Do we need such a test, testing of restarting servers after disposing them?
I thought servers get disposed only when VS Code is closed. #Resolved

Copy link
Author

Choose a reason for hiding this comment

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

No it's so I can test the setting.


In reply to: 369683340 [](ancestors = 369683340)

Copy link

@DonJayamanne DonJayamanne Jan 22, 2020

Choose a reason for hiding this comment

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

Shouldn't that be a separate test? #Resolved

Copy link
Author

Choose a reason for hiding this comment

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

Yeah that would probably be better, especially if I only run these with mocking.


In reply to: 369692742 [](ancestors = 369692742)

@codecov-io
Copy link

codecov-io commented Jan 22, 2020

Codecov Report

❗ No coverage uploaded for pull request base (master@417917c). Click here to learn what that means.
The diff coverage is 41.28%.

Impacted file tree graph

@@           Coverage Diff            @@
##             master   #9708   +/-   ##
========================================
  Coverage          ?   60.6%           
========================================
  Files             ?     552           
  Lines             ?   29833           
  Branches          ?    4514           
========================================
  Hits              ?   18079           
  Misses            ?   10743           
  Partials          ?    1011
Impacted Files Coverage Δ
...terpreter/locators/services/condaEnvFileService.ts 86.27% <ø> (ø)
src/client/common/platform/types.ts 100% <ø> (ø)
src/client/activation/refCountedLanguageServer.ts 37.2% <0%> (ø)
.../datascience/interactive-common/interactiveBase.ts 17.13% <0%> (ø)
src/client/telemetry/index.ts 85.58% <0%> (ø)
src/client/activation/jedi.ts 21.21% <0%> (ø)
src/client/common/platform/fs-temp.ts 100% <100%> (ø)
src/client/common/platform/fileSystem.ts 25.8% <100%> (ø)
src/client/linters/serviceRegistry.ts 100% <100%> (ø)
src/client/common/variables/environment.ts 92.68% <100%> (ø)
... and 7 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 417917c...4414f75. Read the comment docs.

this.notebookProvider.onDidOpenNotebookEditor(this.onDidOpenNotebook.bind(this));
this.interactiveProvider.onDidChangeActiveInteractiveWindow(this.onDidOpenOrCloseInteractive.bind(this));
}
public activate(): Promise<void> {
Copy link
Member

@IanMatthewHuff IanMatthewHuff Jan 22, 2020

Choose a reason for hiding this comment

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

This seems off in remote connection cases. Just making sure that I'm reading this right, but it looks like it would be starting up local servers if you opened a notebook and had just connected it remote. Is that correct? #Resolved

Copy link
Author

Choose a reason for hiding this comment

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

No. The serverstart only starts a local server if the options for starting don't include a server URI


In reply to: 369711529 [](ancestors = 369711529)

Copy link
Author

Choose a reason for hiding this comment

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

Maybe I should update the comment here to say that.


In reply to: 369713520 [](ancestors = 369713520,369711529)

Copy link
Member

@IanMatthewHuff IanMatthewHuff left a comment

Choose a reason for hiding this comment

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

:shipit:

cancelToken?: CancellationToken,
currentKernel?: IJupyterKernelSpec | LiveKernelModel
): Promise<KernelSpecInterpreter> {
let suggestions = await this.selectionProvider.getKernelSelectionsForRemoteSession(session, cancelToken);
suggestions = suggestions.filter(item => !this.kernelIdsToHide.has(item.selection.kernelModel?.id || ''));
return this.selectKernel(suggestions, session, cancelToken, currentKernel);
return this.selectKernel(suggestions, session, disableUI, cancelToken, currentKernel);
Copy link

@DonJayamanne DonJayamanne Jan 22, 2020

Choose a reason for hiding this comment

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

Why not just avoid calling these methods. After all, selectRemoteKernel will display a picker, thats what its mean to do.
We can avoid passing these flags by not calling the methods higher up. #Resolved

cancelToken?: CancellationToken,
currentKernel?: IJupyterKernelSpec | LiveKernelModel
): Promise<KernelSpecInterpreter> {
let suggestions = await this.selectionProvider.getKernelSelectionsForLocalSession(session, cancelToken);
suggestions = suggestions.filter(item => !this.kernelIdsToHide.has(item.selection.kernelModel?.id || ''));
return this.selectKernel(suggestions, session, cancelToken, currentKernel);
return this.selectKernel(suggestions, session, disableUI, cancelToken, currentKernel);
Copy link

@DonJayamanne DonJayamanne Jan 22, 2020

Choose a reason for hiding this comment

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

Why not just avoid calling these methods. After all, selectRemoteKernel will display a picker, thats what its mean to do.
We can avoid passing these flags by not calling the methods higher up. #Resolved

} else {
telemetryProps.promptedToSelect = true;
selection = await this.selectLocalKernel(sessionManager, cancelToken);
selection = await this.selectLocalKernel(sessionManager, disableUI, cancelToken);
Copy link

@DonJayamanne DonJayamanne Jan 22, 2020

Choose a reason for hiding this comment

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

No need to call this method as user needs to select a kernel, hence the name selectLocalKernel. #Resolved

@@ -126,7 +132,7 @@ export class JupyterExecutionBase implements IJupyterExecution {
// Get hold of the kernelspec and corresponding (matching) interpreter that'll be used as the spec.
// We can do this in parallel, while starting the server (faster).
traceInfo(`Getting kernel specs for ${options ? options.purpose : 'unknown type of'} server`);
kernelSpecInterpreterPromise = this.kernelSelector.getKernelForLocalConnection(undefined, options?.metadata, kernelSpecCancelSource.token);
kernelSpecInterpreterPromise = this.kernelSelector.getKernelForLocalConnection(undefined, options?.metadata, !allowUI, kernelSpecCancelSource.token);
Copy link

@DonJayamanne DonJayamanne Jan 22, 2020

Choose a reason for hiding this comment

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

Why not just abort starting server if there's no kernel information.
I.e. in non-ui mode we cannot have a server without any kernels, hence we can just abort here (or just after the kernel promise has resolved).
I think that'll be more readable as well, indicating the fact that in non-ui mode it is mandatory. #Resolved


// See if the old options had the same UI setting. If not,
// cancel the old
if (data && data.options.disableUI !== fixedOptions.disableUI) {
Copy link
Author

@rchiodo rchiodo Jan 22, 2020

Choose a reason for hiding this comment

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

I think maybe I should add a unit test for this stuff. It'd be rather hairy to try and get a functional tset with the right timing on this. #Resolved

@rchiodo
Copy link
Author

rchiodo commented Jan 23, 2020

@DonJayamanne did you have any more comments?

@sonarqubecloud
Copy link

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

@rchiodo rchiodo merged commit d5f153e into master Jan 23, 2020
@rchiodo rchiodo deleted the rchiodo/startup_server branch January 23, 2020 23:03
@lock lock bot locked as resolved and limited conversation to collaborators Jan 31, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants