9696
9797from .operations .os_ops import ConnectionParams
9898from .operations .local_ops import LocalOperations
99- from .operations .remote_ops import RemoteOperations
10099
101100InternalError = pglib .InternalError
102101ProgrammingError = pglib .ProgrammingError
@@ -487,7 +486,7 @@ def init(self, initdb_params=None, cached=True, **kwargs):
487486 os_ops = self .os_ops ,
488487 params = initdb_params ,
489488 bin_path = self .bin_dir ,
490- cached = False )
489+ cached = cached )
491490
492491 # initialize default config files
493492 self .default_conf (** kwargs )
@@ -717,9 +716,9 @@ def slow_start(self, replica=False, dbname='template1', username=None, max_attem
717716 OperationalError },
718717 max_attempts = max_attempts )
719718
720- def start (self , params = [] , wait = True ):
719+ def start (self , params = None , wait : bool = True ) -> 'PostgresNode' :
721720 """
722- Starts the PostgreSQL node using pg_ctl if node has not been started.
721+ Starts the PostgreSQL node using pg_ctl if the node has not been started.
723722 By default, it waits for the operation to complete before returning.
724723 Optionally, it can return immediately without waiting for the start operation
725724 to complete by setting the `wait` parameter to False.
@@ -731,45 +730,62 @@ def start(self, params=[], wait=True):
731730 Returns:
732731 This instance of :class:`.PostgresNode`.
733732 """
733+ if params is None :
734+ params = []
734735 if self .is_started :
735736 return self
736737
737738 _params = [
738- self ._get_bin_path ("pg_ctl" ),
739- "-D" , self .data_dir ,
740- "-l" , self .pg_log_file ,
741- "-w" if wait else '-W' , # --wait or --no-wait
742- "start"
743- ] + params # yapf: disable
739+ self ._get_bin_path ("pg_ctl" ),
740+ "-D" , self .data_dir ,
741+ "-l" , self .pg_log_file ,
742+ "-w" if wait else '-W' , # --wait or --no-wait
743+ "start"
744+ ] + params # yapf: disable
744745
745- startup_retries = 5
746- while True :
746+ max_retries = 5
747+ sleep_interval = 5 # seconds
748+
749+ for attempt in range (max_retries ):
747750 try :
748751 exit_status , out , error = execute_utility (_params , self .utils_log_file , verbose = True )
749752 if error and 'does not exist' in error :
750753 raise Exception
754+ break # Exit the loop if successful
751755 except Exception as e :
752- files = self ._collect_special_files ()
753- if any (len (file ) > 1 and 'Is another postmaster already '
754- 'running on port' in file [1 ].decode () for
755- file in files ):
756- logging .warning ("Detected an issue with connecting to port {0}. "
757- "Trying another port after a 5-second sleep..." .format (self .port ))
758- self .port = reserve_port ()
759- options = {'port' : str (self .port )}
760- self .set_auto_conf (options )
761- startup_retries -= 1
762- time .sleep (5 )
763- continue
764-
765- msg = 'Cannot start node'
766- raise_from (StartNodeException (msg , files ), e )
767- break
756+ if self ._handle_port_conflict ():
757+ if attempt < max_retries - 1 :
758+ logging .info (f"Retrying start operation (Attempt { attempt + 2 } /{ max_retries } )..." )
759+ time .sleep (sleep_interval )
760+ continue
761+ else :
762+ logging .error ("Reached maximum retry attempts. Unable to start node." )
763+ raise StartNodeException ("Cannot start node after multiple attempts" ,
764+ self ._collect_special_files ()) from e
765+ raise StartNodeException ("Cannot start node" , self ._collect_special_files ()) from e
766+
768767 self ._maybe_start_logger ()
769768 self .is_started = True
770769 return self
771770
772- def stop (self , params = [], wait = True ):
771+ def _handle_port_conflict (self ) -> bool :
772+ """
773+ Checks for a port conflict and attempts to resolve it by changing the port.
774+ Returns True if the port was changed, False otherwise.
775+ """
776+ files = self ._collect_special_files ()
777+ if any (len (file ) > 1 and 'Is another postmaster already running on port' in file [1 ].decode () for file in files ):
778+ logging .warning (f"Port conflict detected on port { self .port } ." )
779+ if self ._should_free_port :
780+ logging .warning ("Port reservation skipped due to _should_free_port setting." )
781+ return False
782+ self .port = reserve_port ()
783+ self .set_auto_conf ({'port' : str (self .port )})
784+ logging .info (f"Port changed to { self .port } ." )
785+ return True
786+ return False
787+
788+ def stop (self , params = None , wait = True ):
773789 """
774790 Stops the PostgreSQL node using pg_ctl if the node has been started.
775791
@@ -780,6 +796,8 @@ def stop(self, params=[], wait=True):
780796 Returns:
781797 This instance of :class:`.PostgresNode`.
782798 """
799+ if params is None :
800+ params = []
783801 if not self .is_started :
784802 return self
785803
@@ -812,7 +830,7 @@ def kill(self, someone=None):
812830 os .kill (self .auxiliary_pids [someone ][0 ], sig )
813831 self .is_started = False
814832
815- def restart (self , params = [] ):
833+ def restart (self , params = None ):
816834 """
817835 Restart this node using pg_ctl.
818836
@@ -823,6 +841,8 @@ def restart(self, params=[]):
823841 This instance of :class:`.PostgresNode`.
824842 """
825843
844+ if params is None :
845+ params = []
826846 _params = [
827847 self ._get_bin_path ("pg_ctl" ),
828848 "-D" , self .data_dir ,
@@ -844,7 +864,7 @@ def restart(self, params=[]):
844864
845865 return self
846866
847- def reload (self , params = [] ):
867+ def reload (self , params = None ):
848868 """
849869 Asynchronously reload config files using pg_ctl.
850870
@@ -855,6 +875,8 @@ def reload(self, params=[]):
855875 This instance of :class:`.PostgresNode`.
856876 """
857877
878+ if params is None :
879+ params = []
858880 _params = [
859881 self ._get_bin_path ("pg_ctl" ),
860882 "-D" , self .data_dir ,
@@ -1587,7 +1609,7 @@ def pgbench_table_checksums(self, dbname="postgres",
15871609 return {(table , self .table_checksum (table , dbname ))
15881610 for table in pgbench_tables }
15891611
1590- def set_auto_conf (self , options , config = 'postgresql.auto.conf' , rm_options = {} ):
1612+ def set_auto_conf (self , options , config = 'postgresql.auto.conf' , rm_options = None ):
15911613 """
15921614 Update or remove configuration options in the specified configuration file,
15931615 updates the options specified in the options dictionary, removes any options
@@ -1603,6 +1625,8 @@ def set_auto_conf(self, options, config='postgresql.auto.conf', rm_options={}):
16031625 Defaults to an empty set.
16041626 """
16051627 # parse postgresql.auto.conf
1628+ if rm_options is None :
1629+ rm_options = {}
16061630 path = os .path .join (self .data_dir , config )
16071631
16081632 lines = self .os_ops .readlines (path )
0 commit comments