Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit e20ed5e

Browse files
committed
Add functions that return PIDs of various processes
1 parent 5bc608e commit e20ed5e

File tree

3 files changed

+140
-9
lines changed

3 files changed

+140
-9
lines changed

testgres/connection.py

+7
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ def __init__(self, node, dbname=None, username=None, password=None):
3434
username = username or default_username()
3535

3636
self._node = node
37+
self._backend_pid = None
3738

3839
self._connection = pglib.connect(
3940
database=dbname,
@@ -52,6 +53,12 @@ def node(self):
5253
def connection(self):
5354
return self._connection
5455

56+
@property
57+
def backend_pid(self):
58+
if self._backend_pid is None:
59+
self._backend_pid = self.execute("select pg_backend_pid();")[0][0]
60+
return self._backend_pid
61+
5562
@property
5663
def cursor(self):
5764
return self._cursor

testgres/node.py

+81-4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@
55
import six
66
import subprocess
77
import time
8+
import warnings
9+
10+
try:
11+
import psutil
12+
except ImportError:
13+
psutil = None
814

915
from shutil import rmtree
1016
from six import raise_from
@@ -48,7 +54,8 @@
4854
ExecUtilException, \
4955
QueryException, \
5056
StartNodeException, \
51-
TimeoutException
57+
TimeoutException, \
58+
TestgresException
5259

5360
from .logger import TestgresLogger
5461

@@ -116,7 +123,11 @@ def __exit__(self, type, value, traceback):
116123

117124
@property
118125
def pid(self):
119-
return self.get_pid()
126+
return self.get_main_pid()
127+
128+
@property
129+
def auxiliary_pids(self):
130+
return self.get_auxiliary_pids()
120131

121132
@property
122133
def master(self):
@@ -417,7 +428,7 @@ def status(self):
417428
elif e.exit_code == 4:
418429
return NodeStatus.Uninitialized
419430

420-
def get_pid(self):
431+
def get_main_pid(self):
421432
"""
422433
Return postmaster's PID if node is running, else 0.
423434
"""
@@ -428,7 +439,73 @@ def get_pid(self):
428439
return int(f.readline())
429440

430441
# for clarity
431-
return 0
442+
return None
443+
444+
def get_child_processes(self):
445+
''' Returns child processes for this node '''
446+
447+
if psutil is None:
448+
warnings.warn("psutil module is not installed")
449+
return None
450+
451+
try:
452+
postmaster = psutil.Process(self.pid)
453+
except psutil.NoSuchProcess:
454+
return None
455+
456+
return postmaster.children(recursive=True)
457+
458+
def get_auxiliary_pids(self):
459+
''' Returns dict with pids of auxiliary processes '''
460+
461+
children = self.get_child_processes()
462+
if children is None:
463+
return None
464+
465+
result = {}
466+
for child in children:
467+
line = child.cmdline()[0]
468+
if line.startswith('postgres: checkpointer'):
469+
result['checkpointer'] = child.pid
470+
elif line.startswith('postgres: background writer'):
471+
result['bgwriter'] = child.pid
472+
elif line.startswith('postgres: walwriter'):
473+
result['walwriter'] = child.pid
474+
elif line.startswith('postgres: autovacuum launcher'):
475+
result['autovacuum_launcher'] = child.pid
476+
elif line.startswith('postgres: stats collector'):
477+
result['stats'] = child.pid
478+
elif line.startswith('postgres: logical replication launcher'):
479+
result['logical_replication_launcher'] = child.pid
480+
elif line.startswith('postgres: walreceiver'):
481+
result['walreceiver'] = child.pid
482+
elif line.startswith('postgres: walsender'):
483+
result.setdefault('walsenders', [])
484+
result['walsenders'].append(child.pid)
485+
elif line.startswith('postgres: startup'):
486+
result['startup'] = child.pid
487+
488+
return result
489+
490+
def get_walsender_pid(self):
491+
''' Returns pid of according walsender for replica '''
492+
493+
if not self._master:
494+
raise TestgresException("This node is not a replica")
495+
496+
children = self._master.get_child_processes()
497+
if children is None:
498+
return None
499+
500+
sql = 'select application_name, client_port from pg_stat_replication'
501+
for name, client_port in self._master.execute(sql):
502+
if name == self.name:
503+
for child in children:
504+
line = child.cmdline()[0]
505+
if line.startswith('postgres: walsender') and str(client_port) in line:
506+
return child.pid
507+
508+
return None
432509

433510
def get_control_data(self):
434511
"""

tests/test_simple.py

+52-5
Original file line numberDiff line numberDiff line change
@@ -205,27 +205,27 @@ def test_status(self):
205205

206206
# check statuses after each operation
207207
with get_new_node() as node:
208-
self.assertEqual(node.pid, 0)
208+
self.assertIsNone(node.pid)
209209
self.assertEqual(node.status(), NodeStatus.Uninitialized)
210210

211211
node.init()
212212

213-
self.assertEqual(node.pid, 0)
213+
self.assertIsNone(node.pid)
214214
self.assertEqual(node.status(), NodeStatus.Stopped)
215215

216216
node.start()
217217

218-
self.assertNotEqual(node.pid, 0)
218+
self.assertIsNotNone(node.pid)
219219
self.assertEqual(node.status(), NodeStatus.Running)
220220

221221
node.stop()
222222

223-
self.assertEqual(node.pid, 0)
223+
self.assertIsNone(node.pid)
224224
self.assertEqual(node.status(), NodeStatus.Stopped)
225225

226226
node.cleanup()
227227

228-
self.assertEqual(node.pid, 0)
228+
self.assertIsNone(node.pid)
229229
self.assertEqual(node.status(), NodeStatus.Uninitialized)
230230

231231
def test_psql(self):
@@ -702,6 +702,53 @@ def test_version_management(self):
702702
self.assertTrue(b > c)
703703
self.assertTrue(a > c)
704704

705+
def test_pids(self):
706+
try:
707+
import psutil
708+
except ImportError:
709+
psutil = None
710+
711+
master_processes = (
712+
'checkpointer',
713+
'bgwriter',
714+
'walwriter',
715+
'autovacuum_launcher',
716+
'stats',
717+
'logical_replication_launcher',
718+
'walsenders',
719+
)
720+
repl_processes = (
721+
'startup',
722+
'checkpointer',
723+
'bgwriter',
724+
'stats',
725+
'walreceiver',
726+
)
727+
728+
with get_new_node('master') as master:
729+
master.init().start()
730+
731+
self.assertIsNotNone(master.pid)
732+
with master.connect() as con:
733+
self.assertTrue(con.backend_pid > 0)
734+
735+
with master.backup() as backup:
736+
with backup.spawn_replica('repl', True) as repl:
737+
repl.start()
738+
if psutil is None:
739+
self.assertIsNone(master.auxiliary_pids)
740+
self.assertIsNone(repl.auxiliary_pids)
741+
else:
742+
master_pids = master.auxiliary_pids
743+
for name in master_processes:
744+
self.assertTrue(name in master_pids)
745+
self.assertTrue(len(master_pids['walsenders']) == 1)
746+
747+
repl_pids = repl.auxiliary_pids
748+
for name in repl_processes:
749+
self.assertTrue(name in repl_pids)
750+
self.assertTrue(repl.get_walsender_pid() == master_pids['walsenders'][0])
751+
705752

706753
if __name__ == '__main__':
707754
unittest.main()

0 commit comments

Comments
 (0)