96
96
97
97
from .operations .os_ops import ConnectionParams
98
98
from .operations .local_ops import LocalOperations
99
- from .operations .remote_ops import RemoteOperations
100
99
101
100
InternalError = pglib .InternalError
102
101
ProgrammingError = pglib .ProgrammingError
@@ -487,7 +486,7 @@ def init(self, initdb_params=None, cached=True, **kwargs):
487
486
os_ops = self .os_ops ,
488
487
params = initdb_params ,
489
488
bin_path = self .bin_dir ,
490
- cached = False )
489
+ cached = cached )
491
490
492
491
# initialize default config files
493
492
self .default_conf (** kwargs )
@@ -717,9 +716,9 @@ def slow_start(self, replica=False, dbname='template1', username=None, max_attem
717
716
OperationalError },
718
717
max_attempts = max_attempts )
719
718
720
- def start (self , params = [] , wait = True ):
719
+ def start (self , params = None , wait : bool = True ) -> 'PostgresNode' :
721
720
"""
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.
723
722
By default, it waits for the operation to complete before returning.
724
723
Optionally, it can return immediately without waiting for the start operation
725
724
to complete by setting the `wait` parameter to False.
@@ -731,45 +730,62 @@ def start(self, params=[], wait=True):
731
730
Returns:
732
731
This instance of :class:`.PostgresNode`.
733
732
"""
733
+ if params is None :
734
+ params = []
734
735
if self .is_started :
735
736
return self
736
737
737
738
_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
744
745
745
- startup_retries = 5
746
- while True :
746
+ max_retries = 5
747
+ sleep_interval = 5 # seconds
748
+
749
+ for attempt in range (max_retries ):
747
750
try :
748
751
exit_status , out , error = execute_utility (_params , self .utils_log_file , verbose = True )
749
752
if error and 'does not exist' in error :
750
753
raise Exception
754
+ break # Exit the loop if successful
751
755
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
+
768
767
self ._maybe_start_logger ()
769
768
self .is_started = True
770
769
return self
771
770
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 ):
773
789
"""
774
790
Stops the PostgreSQL node using pg_ctl if the node has been started.
775
791
@@ -780,6 +796,8 @@ def stop(self, params=[], wait=True):
780
796
Returns:
781
797
This instance of :class:`.PostgresNode`.
782
798
"""
799
+ if params is None :
800
+ params = []
783
801
if not self .is_started :
784
802
return self
785
803
@@ -812,7 +830,7 @@ def kill(self, someone=None):
812
830
os .kill (self .auxiliary_pids [someone ][0 ], sig )
813
831
self .is_started = False
814
832
815
- def restart (self , params = [] ):
833
+ def restart (self , params = None ):
816
834
"""
817
835
Restart this node using pg_ctl.
818
836
@@ -823,6 +841,8 @@ def restart(self, params=[]):
823
841
This instance of :class:`.PostgresNode`.
824
842
"""
825
843
844
+ if params is None :
845
+ params = []
826
846
_params = [
827
847
self ._get_bin_path ("pg_ctl" ),
828
848
"-D" , self .data_dir ,
@@ -844,7 +864,7 @@ def restart(self, params=[]):
844
864
845
865
return self
846
866
847
- def reload (self , params = [] ):
867
+ def reload (self , params = None ):
848
868
"""
849
869
Asynchronously reload config files using pg_ctl.
850
870
@@ -855,6 +875,8 @@ def reload(self, params=[]):
855
875
This instance of :class:`.PostgresNode`.
856
876
"""
857
877
878
+ if params is None :
879
+ params = []
858
880
_params = [
859
881
self ._get_bin_path ("pg_ctl" ),
860
882
"-D" , self .data_dir ,
@@ -1587,7 +1609,7 @@ def pgbench_table_checksums(self, dbname="postgres",
1587
1609
return {(table , self .table_checksum (table , dbname ))
1588
1610
for table in pgbench_tables }
1589
1611
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 ):
1591
1613
"""
1592
1614
Update or remove configuration options in the specified configuration file,
1593
1615
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={}):
1603
1625
Defaults to an empty set.
1604
1626
"""
1605
1627
# parse postgresql.auto.conf
1628
+ if rm_options is None :
1629
+ rm_options = {}
1606
1630
path = os .path .join (self .data_dir , config )
1607
1631
1608
1632
lines = self .os_ops .readlines (path )
0 commit comments