@@ -11,23 +11,17 @@ import {
11
11
} from "@gitpod/supervisor-api-grpc/lib/status_pb" ;
12
12
import { WorkspaceInfoResponse } from "@gitpod/supervisor-api-grpc/lib/info_pb" ;
13
13
import { workspaceUrl } from "../shared/urls" ;
14
+ import { FrontendDashboardServiceClient } from "../shared/frontend-dashboard-service" ;
15
+ import { Timeout } from "@gitpod/gitpod-protocol/lib/util/timeout" ;
14
16
15
17
export class SupervisorServiceClient {
16
- private static _instance : SupervisorServiceClient | undefined ;
17
- static get ( ) : SupervisorServiceClient {
18
- if ( ! SupervisorServiceClient . _instance ) {
19
- SupervisorServiceClient . _instance = new SupervisorServiceClient ( ) ;
20
- }
21
- return SupervisorServiceClient . _instance ;
22
- }
23
-
24
18
readonly supervisorReady = this . checkReady ( "supervisor" ) ;
25
19
readonly ideReady = this . supervisorReady . then ( ( ) => this . checkReady ( "ide" ) ) ;
26
20
readonly contentReady = Promise . all ( [ this . supervisorReady ] ) . then ( ( ) => this . checkReady ( "content" ) ) ;
27
21
readonly getWorkspaceInfoPromise = this . supervisorReady . then ( ( ) => this . getWorkspaceInfo ( ) ) ;
28
22
private _supervisorWillShutdown : Promise < void > | undefined ;
29
23
30
- private constructor ( ) { }
24
+ constructor ( readonly serviceClient : FrontendDashboardServiceClient ) { }
31
25
32
26
public get supervisorWillShutdown ( ) {
33
27
if ( ! this . _supervisorWillShutdown ) {
@@ -84,13 +78,44 @@ export class SupervisorServiceClient {
84
78
if ( kind == "supervisor" ) {
85
79
wait = "" ;
86
80
}
81
+
82
+ // track whenever a) we are done, or b) we try to connect (again)
83
+ const trackCheckReady = ( p : { aborted ?: boolean } , err ?: any ) : void => {
84
+ const props : Record < string , string > = {
85
+ component : "supervisor-frontend" ,
86
+ instanceId : this . serviceClient . latestInfo ?. instanceId ?? "" ,
87
+ userId : this . serviceClient . latestInfo ?. loggedUserId ?? "" ,
88
+ readyKind : kind ,
89
+ } ;
90
+ if ( err ) {
91
+ props . errorName = err . name ;
92
+ props . errorStack = err . message ?? String ( err ) ;
93
+ }
94
+
95
+ props . aborted = String ( ! ! p . aborted ) ;
96
+ props . wait = wait ;
97
+
98
+ this . serviceClient . trackEvent ( {
99
+ event : "supervisor_check_ready" ,
100
+ properties : props ,
101
+ } ) ;
102
+ } ;
103
+
104
+ // setup a timeout, which is meant to re-establish the connection every 5 seconds
105
+ let isError = false ;
106
+ const timeout = new Timeout ( 5000 , ( ) => this . serviceClient . isCheckReadyRetryEnabled ( ) ) ;
87
107
try {
108
+ timeout . restart ( ) ;
109
+
88
110
const wsSupervisorStatusUrl = workspaceUrl . with ( ( ) => {
89
111
return {
90
112
pathname : "/_supervisor/v1/status/" + kind + wait ,
91
113
} ;
92
114
} ) ;
93
- const response = await fetch ( wsSupervisorStatusUrl . toString ( ) , { credentials : "include" } ) ;
115
+ const response = await fetch ( wsSupervisorStatusUrl . toString ( ) , {
116
+ credentials : "include" ,
117
+ signal : timeout . signal ( ) ,
118
+ } ) ;
94
119
let result ;
95
120
if ( response . ok ) {
96
121
result = await response . json ( ) ;
@@ -112,6 +137,16 @@ export class SupervisorServiceClient {
112
137
) ;
113
138
} catch ( e ) {
114
139
console . debug ( `failed to check whether ${ kind } is ready, trying again...` , e ) ;
140
+
141
+ // we want to track this kind of errors, as they are on the critical path (of revealing the workspace)
142
+ isError = true ;
143
+ trackCheckReady ( { aborted : timeout . signal ( ) ?. aborted } , e ) ;
144
+ } finally {
145
+ if ( ! isError ) {
146
+ // make sure we don't track twice in case of an error
147
+ trackCheckReady ( { aborted : timeout . signal ( ) ?. aborted } ) ;
148
+ }
149
+ timeout . clear ( ) ;
115
150
}
116
151
return this . checkReady ( kind , true ) ;
117
152
}
0 commit comments