UNPKG

11.8 kBJavaScriptView Raw
1"use strict";
2// Copyright (c) Jupyter Development Team.
3// Distributed under the terms of the Modified BSD License.
4Object.defineProperty(exports, "__esModule", { value: true });
5exports.SessionManager = void 0;
6const polling_1 = require("@lumino/polling");
7const signaling_1 = require("@lumino/signaling");
8const serverconnection_1 = require("../serverconnection");
9const basemanager_1 = require("../basemanager");
10const default_1 = require("./default");
11const restapi_1 = require("./restapi");
12/**
13 * An implementation of a session manager.
14 */
15class SessionManager extends basemanager_1.BaseManager {
16 /**
17 * Construct a new session manager.
18 *
19 * @param options - The default options for each session.
20 */
21 constructor(options) {
22 var _a;
23 super(options);
24 this._isReady = false;
25 this._sessionConnections = new Set();
26 this._models = new Map();
27 this._runningChanged = new signaling_1.Signal(this);
28 this._connectionFailure = new signaling_1.Signal(this);
29 // We define these here so they bind `this` correctly
30 this._connectToKernel = (options) => {
31 return this._kernelManager.connectTo(options);
32 };
33 this._kernelManager = options.kernelManager;
34 // Start model polling with exponential backoff.
35 this._pollModels = new polling_1.Poll({
36 auto: false,
37 factory: () => this.requestRunning(),
38 frequency: {
39 interval: 10 * 1000,
40 backoff: true,
41 max: 300 * 1000
42 },
43 name: `@jupyterlab/services:SessionManager#models`,
44 standby: (_a = options.standby) !== null && _a !== void 0 ? _a : 'when-hidden'
45 });
46 // Initialize internal data.
47 this._ready = (async () => {
48 await this._pollModels.start();
49 await this._pollModels.tick;
50 if (this._kernelManager.isActive) {
51 await this._kernelManager.ready;
52 }
53 this._isReady = true;
54 })();
55 }
56 /**
57 * Test whether the manager is ready.
58 */
59 get isReady() {
60 return this._isReady;
61 }
62 /**
63 * A promise that fulfills when the manager is ready.
64 */
65 get ready() {
66 return this._ready;
67 }
68 /**
69 * A signal emitted when the running sessions change.
70 */
71 get runningChanged() {
72 return this._runningChanged;
73 }
74 /**
75 * A signal emitted when there is a connection failure.
76 */
77 get connectionFailure() {
78 return this._connectionFailure;
79 }
80 /**
81 * Dispose of the resources used by the manager.
82 */
83 dispose() {
84 if (this.isDisposed) {
85 return;
86 }
87 this._models.clear();
88 this._sessionConnections.forEach(x => x.dispose());
89 this._pollModels.dispose();
90 super.dispose();
91 }
92 /*
93 * Connect to a running session. See also [[connectToSession]].
94 */
95 connectTo(options) {
96 const sessionConnection = new default_1.SessionConnection({
97 ...options,
98 connectToKernel: this._connectToKernel,
99 serverSettings: this.serverSettings
100 });
101 this._onStarted(sessionConnection);
102 if (!this._models.has(options.model.id)) {
103 // We trust the user to connect to an existing session, but we verify
104 // asynchronously.
105 void this.refreshRunning().catch(() => {
106 /* no-op */
107 });
108 }
109 return sessionConnection;
110 }
111 /**
112 * Create an iterator over the most recent running sessions.
113 *
114 * @returns A new iterator over the running sessions.
115 */
116 running() {
117 return this._models.values();
118 }
119 /**
120 * Force a refresh of the running sessions.
121 *
122 * @returns A promise that with the list of running sessions.
123 *
124 * #### Notes
125 * This is not typically meant to be called by the user, since the
126 * manager maintains its own internal state.
127 */
128 async refreshRunning() {
129 await this._pollModels.refresh();
130 await this._pollModels.tick;
131 }
132 /**
133 * Start a new session. See also [[startNewSession]].
134 *
135 * @param createOptions - Options for creating the session
136 *
137 * @param connectOptions - Options for connecting to the session
138 */
139 async startNew(createOptions, connectOptions = {}) {
140 const model = await (0, restapi_1.startSession)(createOptions, this.serverSettings);
141 await this.refreshRunning();
142 return this.connectTo({ ...connectOptions, model });
143 }
144 /**
145 * Shut down a session by id.
146 */
147 async shutdown(id) {
148 await (0, restapi_1.shutdownSession)(id, this.serverSettings);
149 await this.refreshRunning();
150 }
151 /**
152 * Shut down all sessions.
153 *
154 * @returns A promise that resolves when all of the kernels are shut down.
155 */
156 async shutdownAll() {
157 // Update the list of models to make sure our list is current.
158 await this.refreshRunning();
159 // Shut down all models.
160 await Promise.all([...this._models.keys()].map(id => (0, restapi_1.shutdownSession)(id, this.serverSettings)));
161 // Update the list of models to clear out our state.
162 await this.refreshRunning();
163 }
164 /**
165 * Find a session associated with a path and stop it if it is the only session
166 * using that kernel.
167 *
168 * @param path - The path in question.
169 *
170 * @returns A promise that resolves when the relevant sessions are stopped.
171 */
172 async stopIfNeeded(path) {
173 try {
174 const sessions = await (0, restapi_1.listRunning)(this.serverSettings);
175 const matches = sessions.filter(value => value.path === path);
176 if (matches.length === 1) {
177 const id = matches[0].id;
178 await this.shutdown(id);
179 }
180 }
181 catch (error) {
182 /* Always succeed. */
183 }
184 }
185 /**
186 * Find a session by id.
187 */
188 async findById(id) {
189 if (this._models.has(id)) {
190 return this._models.get(id);
191 }
192 await this.refreshRunning();
193 return this._models.get(id);
194 }
195 /**
196 * Find a session by path.
197 */
198 async findByPath(path) {
199 for (const m of this._models.values()) {
200 if (m.path === path) {
201 return m;
202 }
203 }
204 await this.refreshRunning();
205 for (const m of this._models.values()) {
206 if (m.path === path) {
207 return m;
208 }
209 }
210 return undefined;
211 }
212 /**
213 * Execute a request to the server to poll running kernels and update state.
214 */
215 async requestRunning() {
216 var _a, _b;
217 let models;
218 try {
219 models = await (0, restapi_1.listRunning)(this.serverSettings);
220 }
221 catch (err) {
222 // Handle network errors, as well as cases where we are on a
223 // JupyterHub and the server is not running. JupyterHub returns a
224 // 503 (<2.0) or 424 (>2.0) in that case.
225 if (err instanceof serverconnection_1.ServerConnection.NetworkError ||
226 ((_a = err.response) === null || _a === void 0 ? void 0 : _a.status) === 503 ||
227 ((_b = err.response) === null || _b === void 0 ? void 0 : _b.status) === 424) {
228 this._connectionFailure.emit(err);
229 }
230 throw err;
231 }
232 if (this.isDisposed) {
233 return;
234 }
235 if (this._models.size === models.length &&
236 models.every(model => {
237 var _a, _b, _c, _d;
238 const existing = this._models.get(model.id);
239 if (!existing) {
240 return false;
241 }
242 return (((_a = existing.kernel) === null || _a === void 0 ? void 0 : _a.id) === ((_b = model.kernel) === null || _b === void 0 ? void 0 : _b.id) &&
243 ((_c = existing.kernel) === null || _c === void 0 ? void 0 : _c.name) === ((_d = model.kernel) === null || _d === void 0 ? void 0 : _d.name) &&
244 existing.name === model.name &&
245 existing.path === model.path &&
246 existing.type === model.type);
247 })) {
248 // Identical models list (presuming models does not contain duplicate
249 // ids), so just return
250 return;
251 }
252 this._models = new Map(models.map(x => [x.id, x]));
253 this._sessionConnections.forEach(sc => {
254 if (this._models.has(sc.id)) {
255 sc.update(this._models.get(sc.id));
256 }
257 else {
258 sc.dispose();
259 }
260 });
261 this._runningChanged.emit(models);
262 }
263 /**
264 * Handle a session starting.
265 */
266 _onStarted(sessionConnection) {
267 this._sessionConnections.add(sessionConnection);
268 sessionConnection.disposed.connect(this._onDisposed, this);
269 sessionConnection.propertyChanged.connect(this._onChanged, this);
270 sessionConnection.kernelChanged.connect(this._onChanged, this);
271 }
272 _onDisposed(sessionConnection) {
273 this._sessionConnections.delete(sessionConnection);
274 // A session termination emission could mean the server session is deleted,
275 // or that the session JS object is disposed and the session still exists on
276 // the server, so we refresh from the server to make sure we reflect the
277 // server state.
278 void this.refreshRunning().catch(() => {
279 /* no-op */
280 });
281 }
282 _onChanged() {
283 void this.refreshRunning().catch(() => {
284 /* no-op */
285 });
286 }
287}
288exports.SessionManager = SessionManager;
289/**
290 * The namespace for `SessionManager` class statics.
291 */
292(function (SessionManager) {
293 /**
294 * A no-op session manager to be used when starting sessions is not supported.
295 */
296 class NoopManager extends SessionManager {
297 constructor() {
298 super(...arguments);
299 this._readyPromise = new Promise(() => {
300 /* no-op */
301 });
302 }
303 /**
304 * Whether the manager is active.
305 */
306 get isActive() {
307 return false;
308 }
309 /**
310 * Used for testing.
311 */
312 get parentReady() {
313 return super.ready;
314 }
315 /**
316 * Start a new session - throw an error since it is not supported.
317 */
318 async startNew(createOptions, connectOptions = {}) {
319 return Promise.reject(new Error('Not implemented in no-op Session Manager'));
320 }
321 /*
322 * Connect to a running session - throw an error since it is not supported.
323 */
324 connectTo(options) {
325 throw Error('Not implemented in no-op Session Manager');
326 }
327 /**
328 * A promise that fulfills when the manager is ready (never).
329 */
330 get ready() {
331 return this.parentReady.then(() => this._readyPromise);
332 }
333 /**
334 * Shut down a session by id - throw an error since it is not supported.
335 */
336 async shutdown(id) {
337 return Promise.reject(new Error('Not implemented in no-op Session Manager'));
338 }
339 /**
340 * Execute a request to the server to poll running sessions and update state.
341 */
342 async requestRunning() {
343 return Promise.resolve();
344 }
345 }
346 SessionManager.NoopManager = NoopManager;
347})(SessionManager = exports.SessionManager || (exports.SessionManager = {}));
348//# sourceMappingURL=manager.js.map
\No newline at end of file