40
40
from .logger import TestgresLogger
41
41
42
42
from .utils import \
43
+ eprint , \
43
44
get_bin_path , \
44
45
file_tail , \
45
46
pg_version_ge , \
@@ -80,14 +81,18 @@ def __init__(self, name=None, port=None, base_dir=None, use_logging=False):
80
81
use_logging: enable python logging.
81
82
"""
82
83
83
- # public
84
+ # basic
84
85
self .host = '127.0.0.1'
85
86
self .name = name or generate_app_name ()
86
87
self .port = port or reserve_port ()
87
88
self .base_dir = base_dir
88
89
90
+ # defaults for __exit__()
91
+ self .cleanup_on_good_exit = TestgresConfig .node_cleanup_on_good_exit
92
+ self .cleanup_on_bad_exit = TestgresConfig .node_cleanup_on_bad_exit
93
+ self .shutdown_max_attempts = 3
94
+
89
95
# private
90
- self ._should_rm_dirs = base_dir is None
91
96
self ._should_free_port = port is None
92
97
self ._use_logging = use_logging
93
98
self ._logger = None
@@ -100,12 +105,23 @@ def __enter__(self):
100
105
return self
101
106
102
107
def __exit__ (self , type , value , traceback ):
103
- # stop node if necessary
104
- self .cleanup ()
105
-
106
- # free port if necessary
107
108
self .free_port ()
108
109
110
+ got_exception = value is not None
111
+ c1 = self .cleanup_on_good_exit and not got_exception
112
+ c2 = self .cleanup_on_bad_exit and got_exception
113
+
114
+ attempts = self .shutdown_max_attempts
115
+
116
+ if c1 or c2 :
117
+ self .cleanup (attempts )
118
+ else :
119
+ self ._try_shutdown (attempts )
120
+
121
+ @property
122
+ def pid (self ):
123
+ return self .get_pid ()
124
+
109
125
@property
110
126
def master (self ):
111
127
return self ._master
@@ -126,6 +142,22 @@ def utils_log_name(self):
126
142
def pg_log_name (self ):
127
143
return os .path .join (self .logs_dir , PG_LOG_FILE )
128
144
145
+ def _try_shutdown (self , max_attempts ):
146
+ attempts = 0
147
+
148
+ # try stopping server N times
149
+ while attempts < max_attempts :
150
+ try :
151
+ self .stop ()
152
+ break # OK
153
+ except ExecUtilException :
154
+ pass # one more time
155
+ except Exception :
156
+ # TODO: probably kill stray instance
157
+ eprint ('cannot stop node {}' .format (self .name ))
158
+
159
+ attempts += 1
160
+
129
161
def _assign_master (self , master ):
130
162
"""NOTE: this is a private method!"""
131
163
@@ -163,8 +195,6 @@ def _create_recovery_conf(self, username):
163
195
self .append_conf (RECOVERY_CONF_FILE , line )
164
196
165
197
def _prepare_dirs (self ):
166
- """NOTE: this is a private method!"""
167
-
168
198
if not self .base_dir :
169
199
self .base_dir = tempfile .mkdtemp ()
170
200
@@ -175,23 +205,17 @@ def _prepare_dirs(self):
175
205
os .makedirs (self .logs_dir )
176
206
177
207
def _maybe_start_logger (self ):
178
- """NOTE: this is a private method!"""
179
-
180
208
if self ._use_logging :
181
- # spawn new logger if it doesn't exist or stopped
209
+ # spawn new logger if it doesn't exist or is stopped
182
210
if not self ._logger or not self ._logger .is_alive ():
183
211
self ._logger = TestgresLogger (self .name , self .pg_log_name )
184
212
self ._logger .start ()
185
213
186
214
def _maybe_stop_logger (self ):
187
- """NOTE: this is a private method!"""
188
-
189
215
if self ._logger :
190
216
self ._logger .stop ()
191
217
192
218
def _format_verbose_error (self , message = None ):
193
- """NOTE: this is a private method!"""
194
-
195
219
# list of important files + N of last lines
196
220
files = [
197
221
(os .path .join (self .data_dir , PG_CONF_FILE ), 0 ),
@@ -560,14 +584,16 @@ def pg_ctl(self, params):
560
584
def free_port (self ):
561
585
"""
562
586
Reclaim port owned by this node.
587
+ NOTE: does not free auto selected ports.
563
588
"""
564
589
565
590
if self ._should_free_port :
566
591
release_port (self .port )
567
592
568
593
def cleanup (self , max_attempts = 3 ):
569
594
"""
570
- Stop node if needed and remove its data directory.
595
+ Stop node if needed and remove its data/logs directory.
596
+ NOTE: take a look at TestgresConfig.node_cleanup_full.
571
597
572
598
Args:
573
599
max_attempts: how many times should we try to stop()?
@@ -576,30 +602,15 @@ def cleanup(self, max_attempts=3):
576
602
This instance of PostgresNode.
577
603
"""
578
604
579
- attempts = 0
605
+ self . _try_shutdown ( max_attempts )
580
606
581
- # try stopping server
582
- while attempts < max_attempts :
583
- try :
584
- self .stop ()
585
- break # OK
586
- except ExecUtilException :
587
- pass # one more time
588
- except Exception :
589
- print ('cannot stop node {}' .format (self .name ))
590
-
591
- attempts += 1
592
-
593
- # remove directory tree if necessary
594
- if self ._should_rm_dirs :
595
-
596
- # choose directory to be removed
597
- if TestgresConfig .node_cleanup_full :
598
- rm_dir = self .base_dir # everything
599
- else :
600
- rm_dir = self .data_dir # just data, save logs
607
+ # choose directory to be removed
608
+ if TestgresConfig .node_cleanup_full :
609
+ rm_dir = self .base_dir # everything
610
+ else :
611
+ rm_dir = self .data_dir # just data, save logs
601
612
602
- shutil .rmtree (rm_dir , ignore_errors = True )
613
+ shutil .rmtree (rm_dir , ignore_errors = True )
603
614
604
615
return self
605
616
@@ -750,8 +761,8 @@ def poll_query_until(self,
750
761
raise_programming_error = True ,
751
762
raise_internal_error = True ):
752
763
"""
753
- Run a query once a second until it returs 'expected'.
754
- Query should return single column.
764
+ Run a query once per second until it returns 'expected'.
765
+ Query should return a single value (1 row, 1 column) .
755
766
756
767
Args:
757
768
query: query to be executed.
0 commit comments