Skip to content

Commit a61a0d7

Browse files
committed
compute server; reimplement state of doc editing using conat
1 parent e52b62b commit a61a0d7

File tree

4 files changed

+139
-75
lines changed

4 files changed

+139
-75
lines changed

src/packages/conat/compute/manager.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ is used to edit a given file.
55
66
Access this in the browser for the project you have open:
77
8-
> m = await cc.client.conat_client.computeServerManager({project_id:cc.current().project_id})
8+
m = cc.redux.getProjectActions(cc.current().project_id).computeServerManager
99
1010
*/
1111

src/packages/conat/project/api/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export interface ProjectApi {
1111
jupyter: Jupyter;
1212
sync: Sync;
1313
isReady: () => Promise<boolean>;
14-
waitUntilReady: () => Promise<void>;
14+
waitUntilReady: (opts?: { timeout?: number }) => Promise<void>;
1515
}
1616

1717
const ProjectApiStructure = {

src/packages/frontend/compute/doc-status.tsx

Lines changed: 44 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,6 @@ server. It does the following:
77
88
- If id is as requested and is not the project, draw line in color of that compute server.
99
10-
- If not where we want to be, defines how close via a percentage
11-
12-
- If compute server not running:
13-
- if exists and you own it, prompts user to start it and also shows the
14-
compute server's component so they can do so.
15-
- if not exists (or deleted), say so
16-
- if owned by somebody else, say so
1710
*/
1811

1912
import Inline from "./inline";
@@ -29,6 +22,7 @@ import { avatar_fontcolor } from "@cocalc/frontend/account/avatar/font-color";
2922
import { DisplayImage } from "./select-image";
3023
import Menu from "./menu";
3124
import { SpendLimitStatus } from "./spend-limit";
25+
import useComputeServerApiState from "./use-compute-server-api-state";
3226

3327
interface Props {
3428
project_id: string;
@@ -48,6 +42,11 @@ export function ComputeServerDocStatus({
4842
if (requestedId == null) {
4943
requestedId = id;
5044
}
45+
const apiState = useComputeServerApiState({
46+
project_id,
47+
compute_server_id: requestedId,
48+
});
49+
5150
const [showDetails, setShowDetails] = useState<boolean | null>(null);
5251
const computeServers = useTypedRedux({ project_id }, "compute_servers");
5352
const account_id = useTypedRedux("account", "account_id");
@@ -210,11 +209,15 @@ export function ComputeServerDocStatus({
210209
const { progress, message, status } = getProgress(
211210
server,
212211
account_id,
213-
id,
214212
requestedId,
213+
apiState,
215214
);
216215
if (!showDetails) {
217-
if (showDetails == null && progress < 100) {
216+
if (
217+
showDetails == null &&
218+
progress < 100 &&
219+
(apiState != null || status == "exception")
220+
) {
218221
setShowDetails(true);
219222
}
220223
return topBar(progress);
@@ -283,34 +286,35 @@ export function ComputeServerDocStatus({
283286
);
284287
}
285288

286-
const ENABLED = true;
289+
// gets progress of starting the compute server with given id and
290+
// having it actively available to host this file.
287291

288-
// gets progress of starting the compute server with given id and having it actively available to host this file.
289292
function getProgress(
290293
server: ComputeServerUserInfo | undefined,
291294
account_id,
292-
id,
293295
requestedId,
296+
apiState,
294297
): {
295298
progress: number;
296299
message: string;
297300
status: "exception" | "active" | "normal" | "success";
298301
} {
299-
if (ENABLED) {
300-
return {
301-
progress: 100,
302-
message: "Compute server is fully connected!",
303-
status: "success",
304-
};
305-
}
306-
307302
if (requestedId == 0) {
308303
return {
309304
progress: 50,
310305
message: "Moving back to project...",
311306
status: "active",
312307
};
313308
}
309+
310+
if (apiState == "running") {
311+
return {
312+
progress: 100,
313+
message: "Compute server is fully connected!",
314+
status: "success",
315+
};
316+
}
317+
314318
if (server == null) {
315319
return {
316320
progress: 0,
@@ -330,17 +334,26 @@ function getProgress(
330334
if (
331335
server.account_id != account_id &&
332336
server.state != "running" &&
333-
server.state != "starting"
337+
server.state != "starting" &&
338+
!server.configuration?.allowCollaboratorControl
334339
) {
335340
return {
336341
progress: 0,
337342
message:
338-
"This is not your compute server, and it is not running. Only the owner of a compute server can start it.",
343+
"This is not your compute server, and it is not running. Only the owner of this compute server can start it.",
339344
status: "exception",
340345
};
341346
}
342347

343-
// below here it isn't our server, it is running.
348+
if (apiState != null) {
349+
if (apiState == "starting") {
350+
return {
351+
progress: 75,
352+
message: "Compute server is starting.",
353+
status: "active",
354+
};
355+
}
356+
}
344357

345358
if (server.state == "deprovisioned") {
346359
return {
@@ -381,62 +394,20 @@ function getProgress(
381394
};
382395
}
383396

384-
// below it is running
385-
const computeIsLive = server.detailed_state?.compute?.state == "ready";
386-
if (computeIsLive) {
387-
if (id == requestedId) {
388-
return {
389-
progress: 100,
390-
message: "Compute server is fully connected!",
391-
status: "success",
392-
};
393-
} else {
394-
return {
395-
progress: 90,
396-
message:
397-
"Compute server is connected and should attach to this file soon...",
398-
status: "success",
399-
};
400-
}
401-
}
402-
const filesystemIsLive =
403-
server.detailed_state?.["filesystem-sync"]?.state == "ready";
404-
const computeIsRecent = isRecent(server.detailed_state?.compute?.time);
405-
const filesystemIsRecent = isRecent(
406-
server.detailed_state?.["filesystem-sync"]?.time,
407-
);
408-
if (filesystemIsRecent) {
409-
return {
410-
progress: 70,
411-
message: "Waiting for filesystem to connect.",
412-
status: "normal",
413-
};
414-
}
415-
if (filesystemIsLive) {
416-
if (computeIsRecent) {
417-
return {
418-
progress: 80,
419-
message: "Waiting for compute to connect.",
420-
status: "normal",
421-
};
422-
}
423-
}
424-
425397
return {
426398
progress: 50,
427-
message:
428-
"Compute server is running, but filesystem and compute components aren't connected. Waiting...",
399+
message: "Compute server is starting...",
429400
status: "active",
430401
};
431402
}
432403

433404
// This is useful elsewhere to give a sense of how the compute server
434405
// is doing as it progresses from running to really being fully available.
435-
function getRunningStatus(server) {
406+
function getRunningStatus(server, apiState) {
436407
if (server == null) {
437408
return { progress: 0, message: "Loading...", status: "exception" };
438409
}
439-
return getProgress(server, webapp_client.account_id, server.id, server.id);
410+
return getProgress(server, webapp_client.account_id, server.id, apiState);
440411
}
441412

442413
export function RunningProgress({
@@ -446,8 +417,12 @@ export function RunningProgress({
446417
server: ComputeServerUserInfo | undefined;
447418
style?;
448419
}) {
420+
const apiState = useComputeServerApiState({
421+
project_id: server?.project_id,
422+
compute_server_id: server?.id,
423+
});
449424
const { progress, message } = useMemo(() => {
450-
return getRunningStatus(server);
425+
return getRunningStatus(server, apiState);
451426
}, [server]);
452427

453428
return (
@@ -461,7 +436,3 @@ export function RunningProgress({
461436
</Tooltip>
462437
);
463438
}
464-
465-
function isRecent(expire = 0) {
466-
return Date.now() - expire < 60 * 1000;
467-
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
3+
- If compute server api isReady() returns true, then 'running'.
4+
5+
- If compute server api not ready, check state of compute server itself, as stored
6+
in the database (this is managed by a backend service).
7+
If compute server state is 'starting' or 'running', then api state is 'starting',
8+
otherwise api state is 'off'.
9+
10+
- If the server is in the running state but isReady() returned false, we
11+
wait a few seconds for the api to be ready, then try again.
12+
*/
13+
14+
import { webapp_client } from "@cocalc/frontend/webapp-client";
15+
import { getServerState } from "./api";
16+
import { useEffect, useRef, useState } from "react";
17+
import { until } from "@cocalc/util/async-utils";
18+
import { useIsMountedRef } from "@cocalc/frontend/app-framework";
19+
20+
export type State = "off" | "starting" | "running";
21+
const TIMEOUT = 5000;
22+
23+
export default function useComputeServerApiState({
24+
project_id,
25+
compute_server_id,
26+
}: {
27+
project_id?: string;
28+
compute_server_id?: number;
29+
}): State | null {
30+
const isMountedRef = useIsMountedRef();
31+
const [state, setState] = useState<State | null>(null);
32+
const currentRef = useRef<{
33+
project_id?: string;
34+
compute_server_id?: number;
35+
}>({
36+
project_id,
37+
compute_server_id,
38+
});
39+
currentRef.current.project_id = project_id;
40+
currentRef.current.compute_server_id = compute_server_id;
41+
42+
useEffect(() => {
43+
if (project_id == null || compute_server_id == null) {
44+
setState(null);
45+
return;
46+
}
47+
const isCurrent = () =>
48+
isMountedRef.current &&
49+
currentRef.current.project_id == project_id &&
50+
currentRef.current.compute_server_id == compute_server_id;
51+
const projectApi = webapp_client.conat_client.projectApi({
52+
project_id,
53+
compute_server_id,
54+
});
55+
(async () => {
56+
await until(
57+
async () => {
58+
if (!isCurrent()) return true;
59+
if (await projectApi.isReady()) {
60+
if (!isCurrent()) return true;
61+
setState("running");
62+
return false;
63+
}
64+
const s = await getServerState(compute_server_id);
65+
if (!isCurrent()) return true;
66+
if (s == "running" || s == "starting") {
67+
setState("starting");
68+
} else {
69+
setState("off");
70+
}
71+
72+
// watch for change to running
73+
try {
74+
await projectApi.waitUntilReady({ timeout: TIMEOUT });
75+
if (!isCurrent()) return true;
76+
// didn't throw and is current, so must be running
77+
setState("running");
78+
return false;
79+
} catch {}
80+
if (!isCurrent()) return true;
81+
return false;
82+
},
83+
{ min: 3000, max: 6000 },
84+
);
85+
})();
86+
87+
return () => {
88+
setState(null);
89+
};
90+
}, [project_id, compute_server_id]);
91+
92+
return state;
93+
}

0 commit comments

Comments
 (0)