Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitc86c0bf

Browse files
committed
Merge branch 'ildus-master'
2 parents5bc608e +e46f239 commitc86c0bf

File tree

6 files changed

+230
-23
lines changed

6 files changed

+230
-23
lines changed

‎Dockerfile.tmpl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ FROM postgres:${PG_VERSION}-alpine
22

33
ENV PYTHON=python${PYTHON_VERSION}
44
RUN if [ "${PYTHON_VERSION}" = "2" ] ; then \
5-
apk add --no-cache curl python2 py-virtualenv py-pip; \
5+
apk add --no-cache curl python2 python2-dev build-base musl-dev \
6+
linux-headers py-virtualenv py-pip; \
67
fi
78
RUN if [ "${PYTHON_VERSION}" = "3" ] ; then \
8-
apk add --no-cache curl python3 py-virtualenv; \
9+
apk add --no-cache curl python3 python3-dev build-base musl-dev \
10+
linux-headers py-virtualenv; \
911
fi
1012
ENV LANG=C.UTF-8
1113

‎run_tests.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export VIRTUAL_ENV_DISABLE_PROMPT=1
2222
source$VENV_PATH/bin/activate
2323

2424
# install utilities
25-
$PIP install coverage flake8
25+
$PIP install coverage flake8 psutil
2626

2727
# install testgres' dependencies
2828
export PYTHONPATH=$(pwd)

‎testgres/connection.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ def node(self):
5252
defconnection(self):
5353
returnself._connection
5454

55+
@property
56+
defpid(self):
57+
returnself.execute("select pg_backend_pid()")[0][0]
58+
5559
@property
5660
defcursor(self):
5761
returnself._cursor

‎testgres/enums.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
fromenumimportEnum,IntEnum
2+
fromsiximportiteritems
23

34

45
classXLogMethod(Enum):
@@ -35,3 +36,53 @@ def __bool__(self):
3536

3637
# for Python 2.x
3738
__nonzero__=__bool__
39+
40+
41+
classProcessType(Enum):
42+
"""
43+
Types of processes
44+
"""
45+
46+
AutovacuumLauncher='autovacuum launcher'
47+
BackgroundWriter='background writer'
48+
Checkpointer='checkpointer'
49+
LogicalReplicationLauncher='logical replication launcher'
50+
Startup='startup'
51+
StatsCollector='stats collector'
52+
WalReceiver='wal receiver'
53+
WalSender='wal sender'
54+
WalWriter='wal writer'
55+
56+
# special value
57+
Unknown='unknown'
58+
59+
@staticmethod
60+
deffrom_process(process):
61+
# yapf: disable
62+
# legacy names for older releases of PG
63+
alternative_names= {
64+
ProcessType.LogicalReplicationLauncher: [
65+
'logical replication worker'
66+
],
67+
ProcessType.BackgroundWriter: [
68+
'writer'
69+
],
70+
}
71+
72+
# we deliberately cut special words and spaces
73+
cmdline=''.join(process.cmdline()) \
74+
.replace('postgres:','',1) \
75+
.replace('bgworker:','',1) \
76+
.replace(' ','')
77+
78+
forptypeinProcessType:
79+
ifcmdline.startswith(ptype.value.replace(' ','')):
80+
returnptype
81+
82+
forptype,namesiniteritems(alternative_names):
83+
fornameinnames:
84+
ifcmdline.startswith(name.replace(' ','')):
85+
returnptype
86+
87+
# default
88+
returnProcessType.Unknown

‎testgres/node.py

Lines changed: 109 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@
22

33
importio
44
importos
5-
importsix
65
importsubprocess
76
importtime
87

98
fromshutilimportrmtree
10-
fromsiximportraise_from
9+
fromsiximportraise_from,iteritems
1110
fromtempfileimportmkstemp,mkdtemp
1211

13-
from .enumsimportNodeStatus
12+
from .enumsimportNodeStatus,ProcessType
1413

1514
from .cacheimportcached_initdb
1615

@@ -48,7 +47,8 @@
4847
ExecUtilException, \
4948
QueryException, \
5049
StartNodeException, \
51-
TimeoutException
50+
TimeoutException, \
51+
TestgresException
5252

5353
from .loggerimportTestgresLogger
5454

@@ -64,6 +64,28 @@
6464
from .backupimportNodeBackup
6565

6666

67+
classProcessProxy(object):
68+
"""
69+
Wrapper for psutil.Process
70+
71+
Attributes:
72+
process: wrapped psutill.Process object
73+
ptype: instance of ProcessType
74+
"""
75+
76+
def__init__(self,process):
77+
self.process=process
78+
self.ptype=ProcessType.from_process(process)
79+
80+
def__getattr__(self,name):
81+
returngetattr(self.process,name)
82+
83+
def__str__(self):
84+
pid=self.process.pid
85+
cmdline=' '.join(self.process.cmdline()).strip()
86+
return'{} [{}]'.format(cmdline,pid)
87+
88+
6789
classPostgresNode(object):
6890
def__init__(self,name=None,port=None,base_dir=None):
6991
"""
@@ -116,7 +138,88 @@ def __exit__(self, type, value, traceback):
116138

117139
@property
118140
defpid(self):
119-
returnself.get_pid()
141+
"""
142+
Return postmaster's PID if node is running, else 0.
143+
"""
144+
145+
ifself.status():
146+
pid_file=os.path.join(self.data_dir,PG_PID_FILE)
147+
withio.open(pid_file)asf:
148+
returnint(f.readline())
149+
150+
# for clarity
151+
return0
152+
153+
@property
154+
defauxiliary_pids(self):
155+
"""
156+
Returns a dict of { ProcessType : PID }.
157+
"""
158+
159+
result= {}
160+
161+
forprocessinself.auxiliary_processes:
162+
ifprocess.ptypenotinresult:
163+
result[process.ptype]= []
164+
165+
result[process.ptype].append(process.pid)
166+
167+
returnresult
168+
169+
@property
170+
defauxiliary_processes(self):
171+
"""
172+
Returns a list of auxiliary processes.
173+
Each process is represented by ProcessProxy object.
174+
"""
175+
176+
defis_aux(process):
177+
returnprocess.ptype!=ProcessType.Unknown
178+
179+
returnlist(filter(is_aux,self.child_processes))
180+
181+
@property
182+
defchild_processes(self):
183+
"""
184+
Returns a list of all child processes.
185+
Each process is represented by ProcessProxy object.
186+
"""
187+
188+
try:
189+
importpsutil
190+
exceptImportError:
191+
raiseTestgresException("psutil module is not installed")
192+
193+
# get a list of postmaster's children
194+
children=psutil.Process(self.pid).children()
195+
196+
return [ProcessProxy(p)forpinchildren]
197+
198+
@property
199+
defsource_walsender(self):
200+
"""
201+
Returns master's walsender feeding this replica.
202+
"""
203+
204+
sql="""
205+
select pid
206+
from pg_catalog.pg_stat_replication
207+
where application_name = $1
208+
"""
209+
210+
ifnotself.master:
211+
raiseTestgresException("Node doesn't have a master")
212+
213+
# master should be on the same host
214+
assertself.master.host==self.host
215+
216+
withself.master.connect()ascon:
217+
forrowincon.execute(sql,self.name):
218+
forchildinself.master.auxiliary_processes:
219+
ifchild.pid==int(row[0]):
220+
returnchild
221+
222+
raiseQueryException("Master doesn't send WAL to {}",self.name)
120223

121224
@property
122225
defmaster(self):
@@ -417,19 +520,6 @@ def status(self):
417520
elife.exit_code==4:
418521
returnNodeStatus.Uninitialized
419522

420-
defget_pid(self):
421-
"""
422-
Return postmaster's PID if node is running, else 0.
423-
"""
424-
425-
ifself.status():
426-
pid_file=os.path.join(self.data_dir,PG_PID_FILE)
427-
withio.open(pid_file)asf:
428-
returnint(f.readline())
429-
430-
# for clarity
431-
return0
432-
433523
defget_control_data(self):
434524
"""
435525
Return contents of pg_control file.
@@ -990,7 +1080,7 @@ def pgbench_run(self,
9901080
"-U",username,
9911081
]+options
9921082

993-
forkey,valueinsix.iteritems(kwargs):
1083+
forkey,valueiniteritems(kwargs):
9941084
# rename keys for pgbench
9951085
key=key.replace('_','-')
9961086

‎tests/test_simple.py

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
BackupException, \
2222
QueryException, \
2323
CatchUpException, \
24-
TimeoutException
24+
TimeoutException, \
25+
TestgresException
2526

2627
fromtestgresimport \
2728
TestgresConfig, \
@@ -40,6 +41,7 @@
4041

4142
fromtestgresimportbound_ports
4243
fromtestgres.utilsimportpg_version_ge
44+
fromtestgres.enumsimportProcessType
4345

4446

4547
defutil_exists(util):
@@ -59,6 +61,14 @@ def good_properties(f):
5961
returnTrue
6062

6163

64+
defmodule_exists(module):
65+
try:
66+
__import__(module)
67+
returnTrue
68+
exceptImportError:
69+
returnFalse
70+
71+
6272
@contextmanager
6373
defremoving(f):
6474
try:
@@ -702,6 +712,56 @@ def test_version_management(self):
702712
self.assertTrue(b>c)
703713
self.assertTrue(a>c)
704714

715+
@unittest.skipUnless(module_exists('psutil'),'might be missing')
716+
deftest_child_pids(self):
717+
master_processes= [
718+
ProcessType.AutovacuumLauncher,
719+
ProcessType.BackgroundWriter,
720+
ProcessType.Checkpointer,
721+
ProcessType.StatsCollector,
722+
ProcessType.WalSender,
723+
ProcessType.WalWriter,
724+
]
725+
726+
ifpg_version_ge('10'):
727+
master_processes.append(ProcessType.LogicalReplicationLauncher)
728+
729+
repl_processes= [
730+
ProcessType.Startup,
731+
ProcessType.WalReceiver,
732+
]
733+
734+
withget_new_node().init().start()asmaster:
735+
736+
# master node doesn't have a source walsender!
737+
withself.assertRaises(TestgresException):
738+
master.source_walsender
739+
740+
withmaster.replicate().start()asreplica:
741+
742+
# test __str__ method
743+
str(master.child_processes[0])
744+
745+
master_pids=master.auxiliary_pids
746+
forptypeinmaster_processes:
747+
self.assertIn(ptype,master_pids)
748+
749+
replica_pids=replica.auxiliary_pids
750+
forptypeinrepl_processes:
751+
self.assertIn(ptype,replica_pids)
752+
753+
# there should be exactly 1 source walsender for replica
754+
self.assertEqual(len(master_pids[ProcessType.WalSender]),1)
755+
pid1=master_pids[ProcessType.WalSender][0]
756+
pid2=replica.source_walsender.pid
757+
self.assertEqual(pid1,pid2)
758+
759+
replica.stop()
760+
761+
# there should be no walsender after we've stopped replica
762+
withself.assertRaises(QueryException):
763+
replica.source_walsender
764+
705765

706766
if__name__=='__main__':
707767
unittest.main()

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp