@@ -106,8 +106,10 @@ def __init__(self):
106106 self ._mark_active_interval_sec = 60 * 60 # 1 hour in seconds
107107 self ._new_updates = threading .Event ()
108108 self ._breakpoint_subscription = None
109+ self ._firebase_app = None
109110
110111 # Events for unit testing.
112+ self .connection_complete = threading .Event ()
111113 self .registration_complete = threading .Event ()
112114 self .subscription_complete = threading .Event ()
113115
@@ -116,6 +118,7 @@ def __init__(self):
116118 #
117119
118120 # Delay before retrying failed request.
121+ self .connect_backoff = backoff .Backoff () # Connect to the DB.
119122 self .register_backoff = backoff .Backoff () # Register debuggee.
120123 self .subscribe_backoff = backoff .Backoff () # Subscribe to updates.
121124 self .update_backoff = backoff .Backoff () # Update breakpoint.
@@ -193,7 +196,10 @@ def SetupAuth(self,
193196 service_account_json_file: JSON file to use for credentials. If not
194197 provided, will default to application default credentials.
195198 database_url: Firebase realtime database URL to be used. If not
196- provided, will default to https://{project_id}-cdbg.firebaseio.com
199+ provided, connect attempts to the following DBs will be made, in
200+ order:
201+ https://{project_id}-cdbg.firebaseio.com
202+ https://{project_id}-default-rtdb.firebaseio.com
197203 Raises:
198204 NoProjectIdError: If the project id cannot be determined.
199205 """
@@ -220,11 +226,7 @@ def SetupAuth(self,
220226 'Please specify the project id using the --project_id flag.' )
221227
222228 self ._project_id = project_id
223-
224- if database_url :
225- self ._database_url = database_url
226- else :
227- self ._database_url = f'https://{ self ._project_id } -cdbg.firebaseio.com'
229+ self ._database_url = database_url
228230
229231 def Start (self ):
230232 """Starts the worker thread."""
@@ -287,15 +289,11 @@ def _MainThreadProc(self):
287289 which will run in its own thread. That thread will be owned by
288290 self._breakpoint_subscription.
289291 """
290- # Note: if self._credentials is None, default app credentials will be used.
291- try :
292- firebase_admin .initialize_app (self ._credentials ,
293- {'databaseURL' : self ._database_url })
294- except ValueError :
295- native .LogWarning (
296- f'Failed to initialize firebase: { traceback .format_exc ()} ' )
297- native .LogError ('Failed to start debugger agent. Giving up.' )
298- return
292+ connection_required , delay = True , 0
293+ while connection_required :
294+ time .sleep (delay )
295+ connection_required , delay = self ._ConnectToDb ()
296+ self .connection_complete .set ()
299297
300298 registration_required , delay = True , 0
301299 while registration_required :
@@ -343,6 +341,45 @@ def _StartMarkActiveTimer(self):
343341 self ._MarkActiveTimerFunc )
344342 self ._mark_active_timer .start ()
345343
344+ def _ConnectToDb (self ):
345+ urls = [self ._database_url ] if self ._database_url is not None else \
346+ [f'https://{ self ._project_id } -cdbg.firebaseio.com' ,
347+ f'https://{ self ._project_id } -default-rtdb.firebaseio.com' ]
348+
349+ for url in urls :
350+ native .LogInfo (f'Attempting to connect to DB with url: { url } ' )
351+
352+ status , firebase_app = self ._TryInitializeDbForUrl (url )
353+ if status :
354+ native .LogInfo (f'Successfully connected to DB with url: { url } ' )
355+ self ._database_url = url
356+ self ._firebase_app = firebase_app
357+ self .connect_backoff .Succeeded ()
358+ return (False , 0 ) # Proceed immediately to registering the debuggee.
359+
360+ return (True , self .connect_backoff .Failed ())
361+
362+ def _TryInitializeDbForUrl (self , database_url ):
363+ # Note: if self._credentials is None, default app credentials will be used.
364+ app = None
365+ try :
366+ app = firebase_admin .initialize_app (
367+ self ._credentials , {'databaseURL' : database_url }, name = 'cdbg' )
368+
369+ if self ._CheckSchemaVersionPresence (app ):
370+ return True , app
371+
372+ except ValueError :
373+ native .LogWarning (
374+ f'Failed to initialize firebase: { traceback .format_exc ()} ' )
375+
376+ # This is the failure path, if we hit here we must cleanup the app handle
377+ if app is not None :
378+ firebase_admin .delete_app (app )
379+ app = None
380+
381+ return False , app
382+
346383 def _RegisterDebuggee (self ):
347384 """Single attempt to register the debuggee.
348385
@@ -371,11 +408,12 @@ def _RegisterDebuggee(self):
371408 else :
372409 debuggee_path = f'cdbg/debuggees/{ self ._debuggee_id } '
373410 native .LogInfo (
374- f'registering at { self ._database_url } , path: { debuggee_path } ' )
411+ f'Registering at { self ._database_url } , path: { debuggee_path } ' )
375412 debuggee_data = copy .deepcopy (debuggee )
376413 debuggee_data ['registrationTimeUnixMsec' ] = {'.sv' : 'timestamp' }
377414 debuggee_data ['lastUpdateTimeUnixMsec' ] = {'.sv' : 'timestamp' }
378- firebase_admin .db .reference (debuggee_path ).set (debuggee_data )
415+ firebase_admin .db .reference (debuggee_path ,
416+ self ._firebase_app ).set (debuggee_data )
379417
380418 native .LogInfo (
381419 f'Debuggee registered successfully, ID: { self ._debuggee_id } ' )
@@ -388,10 +426,21 @@ def _RegisterDebuggee(self):
388426 native .LogInfo (f'Failed to register debuggee: { repr (e )} ' )
389427 return (True , self .register_backoff .Failed ())
390428
429+ def _CheckSchemaVersionPresence (self , firebase_app ):
430+ path = f'cdbg/schema_version'
431+ try :
432+ snapshot = firebase_admin .db .reference (path , firebase_app ).get ()
433+ # The value doesn't matter; just return true if there's any value.
434+ return snapshot is not None
435+ except BaseException as e :
436+ native .LogInfo (
437+ f'Failed to check schema version presence at { path } : { repr (e )} ' )
438+ return False
439+
391440 def _CheckDebuggeePresence (self ):
392441 path = f'cdbg/debuggees/{ self ._debuggee_id } /registrationTimeUnixMsec'
393442 try :
394- snapshot = firebase_admin .db .reference (path ).get ()
443+ snapshot = firebase_admin .db .reference (path , self . _firebase_app ).get ()
395444 # The value doesn't matter; just return true if there's any value.
396445 return snapshot is not None
397446 except BaseException as e :
@@ -402,7 +451,8 @@ def _MarkDebuggeeActive(self):
402451 active_path = f'cdbg/debuggees/{ self ._debuggee_id } /lastUpdateTimeUnixMsec'
403452 try :
404453 server_time = {'.sv' : 'timestamp' }
405- firebase_admin .db .reference (active_path ).set (server_time )
454+ firebase_admin .db .reference (active_path ,
455+ self ._firebase_app ).set (server_time )
406456 except BaseException :
407457 native .LogInfo (
408458 f'Failed to mark debuggee active: { traceback .format_exc ()} ' )
@@ -415,7 +465,7 @@ def _SubscribeToBreakpoints(self):
415465
416466 path = f'cdbg/breakpoints/{ self ._debuggee_id } /active'
417467 native .LogInfo (f'Subscribing to breakpoint updates at { path } ' )
418- ref = firebase_admin .db .reference (path )
468+ ref = firebase_admin .db .reference (path , self . _firebase_app )
419469 try :
420470 self ._breakpoint_subscription = ref .listen (self ._ActiveBreakpointCallback )
421471 return (False , 0 )
@@ -508,15 +558,17 @@ def _TransmitBreakpointUpdates(self):
508558
509559 # First, remove from the active breakpoints.
510560 bp_ref = firebase_admin .db .reference (
511- f'cdbg/breakpoints/{ self ._debuggee_id } /active/{ bp_id } ' )
561+ f'cdbg/breakpoints/{ self ._debuggee_id } /active/{ bp_id } ' ,
562+ self ._firebase_app )
512563 bp_ref .delete ()
513564
514565 summary_data = breakpoint_data
515566 # Save snapshot data for snapshots only.
516567 if is_snapshot :
517568 # Note that there may not be snapshot data.
518569 bp_ref = firebase_admin .db .reference (
519- f'cdbg/breakpoints/{ self ._debuggee_id } /snapshot/{ bp_id } ' )
570+ f'cdbg/breakpoints/{ self ._debuggee_id } /snapshot/{ bp_id } ' ,
571+ self ._firebase_app )
520572 bp_ref .set (breakpoint_data )
521573
522574 # Now strip potential snapshot data.
@@ -527,7 +579,8 @@ def _TransmitBreakpointUpdates(self):
527579
528580 # Then add it to the list of final breakpoints.
529581 bp_ref = firebase_admin .db .reference (
530- f'cdbg/breakpoints/{ self ._debuggee_id } /final/{ bp_id } ' )
582+ f'cdbg/breakpoints/{ self ._debuggee_id } /final/{ bp_id } ' ,
583+ self ._firebase_app )
531584 bp_ref .set (summary_data )
532585
533586 native .LogInfo (f'Breakpoint { bp_id } update transmitted successfully' )
0 commit comments