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

Improve runtime session service stability #5380

Merged
merged 45 commits into from
Nov 28, 2024
Merged

Conversation

seeM
Copy link
Contributor

@seeM seeM commented Nov 15, 2024

The aim of this PR is to improve the stability of the runtime session service, with a slight focus on notebooks.

This is another step toward #2671.

I've also added an extensive suite of unit tests for the runtime session service, for two reasons:

  1. Some of the issues we're seeing with notebooks are intermittent and therefore tricky to reproduce without tests.
  2. I wanted to ensure that I didn't break any existing behavior with these changes as well as more planned changes to add notebook-runtime methods to the extension API.

Since there are a bunch of changes to some core code, I've annotated them with comments.

@seeM seeM force-pushed the 2671-runtime-session-service-tests branch 2 times, most recently from b736859 to 4dc4cbb Compare November 22, 2024 07:34
seeM added 28 commits November 26, 2024 13:31
@seeM seeM force-pushed the 2671-runtime-session-service-tests branch from 36827f0 to 586b7e1 Compare November 26, 2024 11:32
@seeM seeM changed the title Runtime session service unit tests; various fixes & improvements Improve runtime session service stability Nov 26, 2024
@seeM seeM requested a review from jmcphers November 26, 2024 12:27

// Avoid unhandled rejections being logged to the console.
// The actual error-handling is in the catch block above.
currentMessagePromise.catch(() => { });
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Cleaning up some log noise.


// The Positron preview view icon.
const positronPreviewViewIcon = registerIcon('positron-preview-view-icon', Codicon.positronPreviewView, nls.localize('positronPreviewViewIcon', 'View icon of the Positron preview view.'));

export const POSITRON_PREVIEW_PLOTS_IN_VIEWER = 'positron.viewer.interactivePlotsInViewer';
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Needed to move this from browser to common to fix failing tests as well as a linting error that must've snuck through a while ago.

session.session.runtimeMetadata.runtimeId === runtimeId &&
session.session.metadata.sessionMode === LanguageRuntimeSessionMode.Console &&
session.state !== RuntimeState.Exited);
// It's possible that there are multiple consoles for the same runtime,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixes a bug where the old console is returned after a failed restore attempt.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there a repo case for that / is that filed? This mitigation is fine but I think we should also ensure we only create one console in that case.

@@ -367,22 +371,29 @@ export class RuntimeSessionService extends Disposable implements IRuntimeSession
session: ILanguageRuntimeSession, exitReason: RuntimeExitReason): Promise<void> {
// We wait for `onDidEndSession()` rather than `RuntimeState.Exited`, because the former
// generates some Console output that must finish before starting up a new runtime:
let disposable: IDisposable | undefined;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixes a leaked disposable if shutting down errors and the session never exits.

`for the ${metadata.languageName} language.`);
}
}
const runningSessionId = this.validateRuntimeSessionStart(sessionMode, languageRuntime, notebookUri, source);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We now also validate that only one runtime is ever running per notebook URI and returning an existing session if it matches.


// Create a promise that resolves when the runtime is ready to use.
const startPromise = new DeferredPromise<string>();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The start promise was previously always created here but now might be created by the caller, and we don't want to overwrite that.

@@ -814,8 +855,7 @@ export class RuntimeSessionService extends Disposable implements IRuntimeSession
`Creating session for language runtime ` +
`${formatLanguageRuntimeMetadata(runtimeMetadata)} failed. Reason: ${err}`);
startPromise.error(err);
this._startingSessionsBySessionMapKey.delete(sessionMapKey);
this._startingConsolesByLanguageId.delete(runtimeMetadata.languageId);
this.clearStartingSessionMaps(sessionMode, runtimeMetadata, notebookUri);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixes a bug where notebooks weren't cleared from the map, and where a starting notebook could affect a starting console.

// Remove the runtime from the starting runtimes.
this._startingConsolesByLanguageId.delete(session.runtimeMetadata.languageId);
this._startingSessionsBySessionMapKey.delete(sessionMapKey);
this.clearStartingSessionMaps(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixes a bug where notebooks weren't cleared from the map, and where a starting notebook could affect a starting console.

if (session.metadata.sessionMode === LanguageRuntimeSessionMode.Console &&
!this._consoleSessionsByLanguageId.has(session.runtimeMetadata.languageId)) {
this._consoleSessionsByLanguageId.set(session.runtimeMetadata.languageId,
session);
} else if (session.metadata.sessionMode === LanguageRuntimeSessionMode.Notebook &&
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixes a bug where notebooks weren't added to the maps after a restart.

@@ -1079,6 +1240,8 @@ export class RuntimeSessionService extends Disposable implements IRuntimeSession
state === RuntimeState.Ready) {
// The runtime looks like it could handle a restart request, so send
// one over.
this.setStartingSessionMaps(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixes a bug where a second console/notebook could be created if done so while the runtime was in the exited state during a restart.

@seeM seeM marked this pull request as ready for review November 26, 2024 12:28
jmcphers
jmcphers previously approved these changes Nov 27, 2024
Copy link
Collaborator

@jmcphers jmcphers left a comment

Choose a reason for hiding this comment

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

Compiled locally and confirmed tests passing, also spun out a few sessions in a built copy and confirmed that things looked stable.

This LGTM. Thanks so much for improving this and adding some tests.

Before you merge, please run the Full Test Suite on this branch; this code is pretty sensitive due to the amount of async state propagating around.

session.session.runtimeMetadata.runtimeId === runtimeId &&
session.session.metadata.sessionMode === LanguageRuntimeSessionMode.Console &&
session.state !== RuntimeState.Exited);
// It's possible that there are multiple consoles for the same runtime,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there a repo case for that / is that filed? This mitigation is fine but I think we should also ensure we only create one console in that case.

@seeM
Copy link
Contributor Author

seeM commented Nov 28, 2024

E2E tests passed thrice. Unit tests failed in the first two attempts, fixed in 811cc2f and 25cea7f. All green on the third attempt, so we should be good to merge!

Attempt 1: https://github.com/posit-dev/positron/actions/runs/12067290897/attempts/1

Attempt 2: https://github.com/posit-dev/positron/actions/runs/12068410989/attempts/1

Attempt 3: https://github.com/posit-dev/positron/actions/runs/12069245433/attempts/1

@seeM seeM merged commit a36f4d7 into main Nov 28, 2024
10 checks passed
@seeM seeM deleted the 2671-runtime-session-service-tests branch November 28, 2024 13:44
@github-actions github-actions bot locked and limited conversation to collaborators Nov 28, 2024
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.

2 participants