Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/src/packages/project/sage_socket.ts
Views: 687
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45import { getLogger } from "@cocalc/backend/logger";6import { enable_mesg } from "@cocalc/backend/misc_node";7import { CoCalcSocket } from "@cocalc/backend/tcp/enable-messaging-protocol";8import { connectToLockedSocket } from "@cocalc/backend/tcp/locked-socket";9import * as message from "@cocalc/util/message";10import { retry_until_success } from "@cocalc/util/misc";11import * as common from "./common";12import { forget_port, get_port } from "./port_manager";13import {14SAGE_SERVER_MAX_STARTUP_TIME_S,15restart_sage_server,16} from "./sage_restart";17import { getSecretToken } from "./servers/secret-token";18import { CB } from "@cocalc/util/types/callback";1920const winston = getLogger("sage-socket");2122// Get a new connection to the Sage server. If the server23// isn't running, e.g., it was killed due to running out of memory,24// attempt to restart it and try to connect.25export async function get_sage_socket(): Promise<CoCalcSocket> {26let socket: CoCalcSocket | undefined;27const try_to_connect = async (cb: CB) => {28try {29socket = await _get_sage_socket();30cb();31} catch (err) {32// Failed for some reason: try to restart one time, then try again.33// We do this because the Sage server can easily get killed due to out of memory conditions.34// But we don't constantly try to restart the server, since it can easily fail to start if35// there is something wrong with a local Sage install.36// Note that restarting the sage server doesn't impact currently running worksheets (they37// have their own process that isn't killed).38try {39await restart_sage_server();40// success at restarting sage server: *IMMEDIATELY* try to connect41socket = await _get_sage_socket();42cb();43} catch (err) {44// won't actually try to restart if called recently.45cb(err);46}47}48};4950return new Promise((resolve, reject) => {51retry_until_success({52f: try_to_connect,53start_delay: 50,54max_delay: 5000,55factor: 1.5,56max_time: SAGE_SERVER_MAX_STARTUP_TIME_S * 1000,57log(m) {58return winston.debug(`get_sage_socket: ${m}`);59},60cb(err) {61if (socket == null) {62reject("failed to get sage socket");63} else if (err) {64reject(err);65} else {66resolve(socket);67}68},69});70});71}7273async function _get_sage_socket(): Promise<CoCalcSocket> {74winston.debug("get sage server port");75const port = await get_port("sage");76winston.debug("get and unlock socket");77if (port == null) throw new Error("port is null");78try {79const sage_socket: CoCalcSocket | undefined = await connectToLockedSocket({80port,81token: getSecretToken(),82});83winston.debug("Successfully unlocked a sage session connection.");8485winston.debug("request sage session from server.");86enable_mesg(sage_socket);87sage_socket.write_mesg("json", message.start_session({ type: "sage" }));88winston.debug(89"Waiting to read one JSON message back, which will describe the session...."90);91// TODO: couldn't this just hang forever :-(92return new Promise<CoCalcSocket>((resolve) => {93sage_socket.once("mesg", (_type, desc) => {94winston.debug(95`Got message back from Sage server: ${common.json(desc)}`96);97sage_socket.pid = desc.pid;98resolve(sage_socket);99});100});101} catch (err2) {102forget_port("sage");103const msg = `_new_session: sage session denied connection: ${err2}`;104throw new Error(msg);105}106}107108109