@@ -9,6 +9,8 @@ import { log } from "@gitpod/gitpod-protocol/lib/util/logging";
99import * as grpc from "@grpc/grpc-js" ;
1010import { getExperimentsClientForBackend } from "@gitpod/gitpod-protocol/lib/experiments/configcat-server" ;
1111import { TrustedValue } from "@gitpod/gitpod-protocol/lib/util/scrubbing" ;
12+ import { createDebugLogInterceptor } from "@gitpod/gitpod-protocol/lib/util/grpc" ;
13+ import { isConnectionAlive } from "@gitpod/gitpod-protocol/lib/util/grpc" ;
1214
1315export interface SpiceDBClientConfig {
1416 address : string ;
@@ -54,6 +56,7 @@ const DefaultClientOptions: grpc.ClientOptions = {
5456 // default is 30s, which is too long for us during rollouts (where service DNS entries are updated)
5557 "grpc.dns_min_time_between_resolutions_ms" : 2_000 ,
5658} ;
59+ const CLIENT_CLOSE_DELAY = 60_000 ; // 60 [s]
5760
5861export function spiceDBConfigFromEnv ( ) : SpiceDBClientConfig | undefined {
5962 const token = process . env [ "SPICEDB_PRESHARED_KEY" ] ;
@@ -110,9 +113,7 @@ export class SpiceDBClientProvider {
110113 } ) ;
111114
112115 // close client after 2 minutes to make sure most pending requests on the previous client are finished.
113- setTimeout ( ( ) => {
114- this . closeClient ( oldClient ) ;
115- } , 2 * 60 * 1000 ) ;
116+ this . closeClientAfter ( oldClient , CLIENT_CLOSE_DELAY ) ;
116117 }
117118 this . clientOptions = clientOptions ;
118119 // `createClient` will use the `DefaultClientOptions` to create client if the value on Feature Flag is not able to create a client
@@ -132,48 +133,77 @@ export class SpiceDBClientProvider {
132133 } ) ( ) ;
133134 }
134135
135- private closeClient ( client : Client ) {
136- try {
137- client . close ( ) ;
138- } catch ( error ) {
139- log . error ( "[spicedb] Error closing client" , error ) ;
140- }
136+ private closeClientAfter ( client : Client , timeout : number ) : void {
137+ setTimeout ( ( ) => {
138+ try {
139+ client . close ( ) ;
140+ } catch ( error ) {
141+ log . error ( "[spicedb] Error closing client" , error ) ;
142+ }
143+ } , timeout ) ;
141144 }
142145
143146 private createClient ( clientOptions : grpc . ClientOptions ) : Client {
144147 log . debug ( "[spicedb] Creating client" , {
145148 clientOptions : new TrustedValue ( clientOptions ) ,
146149 } ) ;
150+
151+ // Inject debugLogInterceptor to log details especially on failed grpc calls
152+ let client : Client ;
153+ const debugInfo = ( ) : object => {
154+ if ( ! client ) {
155+ return { } ;
156+ }
157+ const ch = client . getChannel ( ) ;
158+ return {
159+ target : ch . getTarget ( ) ,
160+ state : ch . getConnectivityState ( false ) ,
161+ } ;
162+ } ;
163+ const interceptors = [ ...( this . interceptors || [ ] ) , createDebugLogInterceptor ( debugInfo ) ] ;
164+
165+ // Create the actual client
147166 try {
148- return v1 . NewClient (
167+ client = v1 . NewClient (
149168 this . clientConfig . token ,
150169 this . clientConfig . address ,
151170 v1 . ClientSecurity . INSECURE_PLAINTEXT_CREDENTIALS ,
152171 undefined ,
153172 {
154173 ...clientOptions ,
155- interceptors : this . interceptors ,
174+ interceptors,
156175 } ,
157176 ) as Client ;
158177 } catch ( error ) {
159178 log . error ( "[spicedb] Error create client, fallback to default options" , error ) ;
160- return v1 . NewClient (
179+ client = v1 . NewClient (
161180 this . clientConfig . token ,
162181 this . clientConfig . address ,
163182 v1 . ClientSecurity . INSECURE_PLAINTEXT_CREDENTIALS ,
164183 undefined ,
165184 {
166185 ...DefaultClientOptions ,
167- interceptors : this . interceptors ,
186+ interceptors,
168187 } ,
169188 ) as Client ;
170189 }
190+ return client ;
171191 }
172192
173- getClient ( ) : SpiceDBClient {
193+ getClient ( checkClient : boolean = false ) : SpiceDBClient {
174194 if ( ! this . client ) {
175195 this . client = this . createClient ( this . clientOptions ) ;
176196 }
197+
198+ if ( checkClient ) {
199+ const oldClient = this . client ;
200+ if ( ! isConnectionAlive ( oldClient ) ) {
201+ const connectivityState = oldClient . getChannel ( ) . getConnectivityState ( false ) ;
202+ log . warn ( "[spicedb] Client is not alive, creating a new one" , { connectivityState } ) ;
203+ this . closeClientAfter ( oldClient , CLIENT_CLOSE_DELAY ) ;
204+ this . client = this . createClient ( this . clientOptions ) ;
205+ }
206+ }
177207 return this . client . promises ;
178208 }
179209}
0 commit comments