testgres 1.16.0__tar.gz → 1.16.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {testgres-1.16.0/testgres.egg-info → testgres-1.16.2}/PKG-INFO +2 -2
- {testgres-1.16.0 → testgres-1.16.2}/pyproject.toml +1 -1
- {testgres-1.16.0 → testgres-1.16.2}/src/__init__.py +1 -1
- {testgres-1.16.0 → testgres-1.16.2}/src/consts.py +3 -0
- {testgres-1.16.0 → testgres-1.16.2}/src/exceptions.py +2 -2
- testgres-1.16.2/src/impl/platforms/darwin/internal_platform_utils.py +456 -0
- {testgres-1.16.0 → testgres-1.16.2}/src/impl/platforms/internal_platform_utils.py +1 -1
- {testgres-1.16.0 → testgres-1.16.2}/src/impl/platforms/internal_platform_utils_factory.py +4 -0
- {testgres-1.16.0 → testgres-1.16.2}/src/impl/platforms/linux/internal_platform_utils.py +3 -2
- {testgres-1.16.0 → testgres-1.16.2}/src/node.py +297 -175
- {testgres-1.16.0 → testgres-1.16.2}/src/node_app.py +28 -20
- {testgres-1.16.0 → testgres-1.16.2}/src/pubsub.py +10 -8
- {testgres-1.16.0 → testgres-1.16.2}/src/raise_error.py +2 -2
- {testgres-1.16.0 → testgres-1.16.2}/src/utils.py +24 -14
- {testgres-1.16.0 → testgres-1.16.2/testgres.egg-info}/PKG-INFO +2 -2
- {testgres-1.16.0 → testgres-1.16.2}/testgres.egg-info/SOURCES.txt +1 -0
- {testgres-1.16.0 → testgres-1.16.2}/testgres.egg-info/requires.txt +1 -1
- {testgres-1.16.0 → testgres-1.16.2}/tests/test_os_ops_common.py +239 -33
- {testgres-1.16.0 → testgres-1.16.2}/tests/test_os_ops_local.py +11 -4
- {testgres-1.16.0 → testgres-1.16.2}/tests/test_testgres_common.py +2 -2
- {testgres-1.16.0 → testgres-1.16.2}/LICENSE +0 -0
- {testgres-1.16.0 → testgres-1.16.2}/README.md +0 -0
- {testgres-1.16.0 → testgres-1.16.2}/setup.cfg +0 -0
- {testgres-1.16.0 → testgres-1.16.2}/src/api.py +0 -0
- {testgres-1.16.0 → testgres-1.16.2}/src/backup.py +0 -0
- {testgres-1.16.0 → testgres-1.16.2}/src/cache.py +0 -0
- {testgres-1.16.0 → testgres-1.16.2}/src/config.py +0 -0
- {testgres-1.16.0 → testgres-1.16.2}/src/connection.py +0 -0
- {testgres-1.16.0 → testgres-1.16.2}/src/decorators.py +0 -0
- {testgres-1.16.0 → testgres-1.16.2}/src/defaults.py +0 -0
- {testgres-1.16.0 → testgres-1.16.2}/src/enums.py +0 -0
- {testgres-1.16.0 → testgres-1.16.2}/src/impl/file_line_reader.py +0 -0
- {testgres-1.16.0 → testgres-1.16.2}/src/impl/internal_utils.py +0 -0
- {testgres-1.16.0 → testgres-1.16.2}/src/impl/platforms/win32/internal_platform_utils.py +0 -0
- {testgres-1.16.0 → testgres-1.16.2}/src/impl/port_manager__generic.py +0 -0
- {testgres-1.16.0 → testgres-1.16.2}/src/impl/port_manager__generic2.py +0 -0
- {testgres-1.16.0 → testgres-1.16.2}/src/impl/port_manager__this_host.py +0 -0
- {testgres-1.16.0 → testgres-1.16.2}/src/logger.py +0 -0
- {testgres-1.16.0 → testgres-1.16.2}/src/port_manager.py +0 -0
- {testgres-1.16.0 → testgres-1.16.2}/src/standby.py +0 -0
- {testgres-1.16.0 → testgres-1.16.2}/testgres.egg-info/dependency_links.txt +0 -0
- {testgres-1.16.0 → testgres-1.16.2}/testgres.egg-info/top_level.txt +0 -0
- {testgres-1.16.0 → testgres-1.16.2}/tests/test_api.py +0 -0
- {testgres-1.16.0 → testgres-1.16.2}/tests/test_config.py +0 -0
- {testgres-1.16.0 → testgres-1.16.2}/tests/test_os_ops_remote.py +0 -0
- {testgres-1.16.0 → testgres-1.16.2}/tests/test_raise_error.py +0 -0
- {testgres-1.16.0 → testgres-1.16.2}/tests/test_testgres_local.py +0 -0
- {testgres-1.16.0 → testgres-1.16.2}/tests/test_testgres_remote.py +0 -0
- {testgres-1.16.0 → testgres-1.16.2}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: testgres
|
|
3
|
-
Version: 1.16.
|
|
3
|
+
Version: 1.16.2
|
|
4
4
|
Summary: Testing utility for PostgreSQL and its extensions
|
|
5
5
|
Author-email: Postgres Professional <testgres@postgrespro.ru>
|
|
6
6
|
License: PostgreSQL
|
|
@@ -27,7 +27,7 @@ Requires-Dist: port-for>=0.4
|
|
|
27
27
|
Requires-Dist: six>=1.9.0
|
|
28
28
|
Requires-Dist: psutil
|
|
29
29
|
Requires-Dist: packaging
|
|
30
|
-
Requires-Dist: testgres.os_ops<4.0.0,>=3.3.
|
|
30
|
+
Requires-Dist: testgres.os_ops<4.0.0,>=3.3.3
|
|
31
31
|
Dynamic: license-file
|
|
32
32
|
|
|
33
33
|
[](https://github.com/postgrespro/testgres/actions/workflows/ci.yml)
|
|
@@ -60,7 +60,7 @@ from testgres.operations.os_ops import OsProcessController
|
|
|
60
60
|
from testgres.operations.local_ops import LocalOperations
|
|
61
61
|
from testgres.operations.remote_ops import RemoteOperations
|
|
62
62
|
|
|
63
|
-
__version__ = "1.16.
|
|
63
|
+
__version__ = "1.16.2"
|
|
64
64
|
|
|
65
65
|
__all__ = [
|
|
66
66
|
"get_new_node",
|
|
@@ -113,7 +113,7 @@ class QueryTimeoutException(QueryException):
|
|
|
113
113
|
def __init__(
|
|
114
114
|
self,
|
|
115
115
|
message: typing.Optional[str] = None,
|
|
116
|
-
query: typing.Optional[str] = None
|
|
116
|
+
query: typing.Optional[str] = None,
|
|
117
117
|
):
|
|
118
118
|
assert message is None or type(message) is str
|
|
119
119
|
assert query is None or type(query) is str
|
|
@@ -170,7 +170,7 @@ class StartNodeException(TestgresException):
|
|
|
170
170
|
def __init__(
|
|
171
171
|
self,
|
|
172
172
|
message: typing.Optional[str] = None,
|
|
173
|
-
files: typing.Optional[typing.Iterable] = None
|
|
173
|
+
files: typing.Optional[typing.Iterable] = None,
|
|
174
174
|
):
|
|
175
175
|
assert message is None or type(message) is str
|
|
176
176
|
assert files is None or isinstance(files, typing.Iterable)
|
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .. import internal_platform_utils as base
|
|
4
|
+
from ... import internal_utils
|
|
5
|
+
from ....raise_error import RaiseError
|
|
6
|
+
|
|
7
|
+
from testgres.operations.os_ops import OsOperations
|
|
8
|
+
from testgres.operations.os_ops import OsCommandResult
|
|
9
|
+
from testgres.operations.exceptions import ExecUtilException
|
|
10
|
+
from testgres.operations.types import T_OS_EXEC_ENV
|
|
11
|
+
|
|
12
|
+
import re
|
|
13
|
+
import shlex
|
|
14
|
+
import typing
|
|
15
|
+
import time
|
|
16
|
+
import subprocess
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class InternalPlatformUtils(base.InternalPlatformUtils):
|
|
20
|
+
C_MAX_FIND_POSTMASTER_ATTEMPTS = 5
|
|
21
|
+
C_BASH_EXE = "/bin/bash"
|
|
22
|
+
|
|
23
|
+
sm_exec_env: T_OS_EXEC_ENV = {
|
|
24
|
+
"LANG": "en_US.UTF-8",
|
|
25
|
+
"LC_ALL": "en_US.UTF-8",
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
# --------------------------------------------------------------------
|
|
29
|
+
def FindPostmaster(
|
|
30
|
+
self,
|
|
31
|
+
os_ops: OsOperations,
|
|
32
|
+
bin_dir: str,
|
|
33
|
+
data_dir: str
|
|
34
|
+
) -> InternalPlatformUtils.FindPostmasterResult:
|
|
35
|
+
assert isinstance(os_ops, OsOperations)
|
|
36
|
+
assert type(bin_dir) is str
|
|
37
|
+
assert type(data_dir) is str
|
|
38
|
+
assert type(__class__.C_BASH_EXE) is str
|
|
39
|
+
assert type(__class__.sm_exec_env) is dict
|
|
40
|
+
assert len(__class__.C_BASH_EXE) > 0
|
|
41
|
+
assert len(bin_dir) > 0
|
|
42
|
+
assert len(data_dir) > 0
|
|
43
|
+
|
|
44
|
+
failures: typing.List[Exception] = []
|
|
45
|
+
|
|
46
|
+
postmaster_pid: typing.Optional[int] = None
|
|
47
|
+
|
|
48
|
+
nAttempts = 0
|
|
49
|
+
|
|
50
|
+
while True:
|
|
51
|
+
nAttempts += 1
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
postmaster_pid = __class__._FindPostmaster(
|
|
55
|
+
os_ops,
|
|
56
|
+
bin_dir,
|
|
57
|
+
data_dir,
|
|
58
|
+
)
|
|
59
|
+
except Exception as e:
|
|
60
|
+
failures.append(e)
|
|
61
|
+
|
|
62
|
+
log_msg = "FindPostmaster (bin_dir={!r}, data_dir={!r}) detects a problem. Exception {}:\n{}".format(
|
|
63
|
+
bin_dir,
|
|
64
|
+
data_dir,
|
|
65
|
+
type(e).__name__,
|
|
66
|
+
e,
|
|
67
|
+
)
|
|
68
|
+
internal_utils.send_log_debug(log_msg)
|
|
69
|
+
|
|
70
|
+
if nAttempts < __class__.C_MAX_FIND_POSTMASTER_ATTEMPTS:
|
|
71
|
+
time.sleep(0.05)
|
|
72
|
+
continue
|
|
73
|
+
|
|
74
|
+
__class__._find_postmaster__throw_error__fail(
|
|
75
|
+
bin_dir=bin_dir,
|
|
76
|
+
data_dir=data_dir,
|
|
77
|
+
failures=failures,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
break
|
|
81
|
+
|
|
82
|
+
if postmaster_pid is None:
|
|
83
|
+
return InternalPlatformUtils.FindPostmasterResult.create_not_found()
|
|
84
|
+
|
|
85
|
+
assert type(postmaster_pid) is int
|
|
86
|
+
|
|
87
|
+
return InternalPlatformUtils.FindPostmasterResult.create_ok(postmaster_pid)
|
|
88
|
+
|
|
89
|
+
# --------------------------------------------------------------------
|
|
90
|
+
@staticmethod
|
|
91
|
+
def _FindPostmaster(
|
|
92
|
+
os_ops: OsOperations,
|
|
93
|
+
bin_dir: str,
|
|
94
|
+
data_dir: str
|
|
95
|
+
) -> typing.Optional[int]:
|
|
96
|
+
assert isinstance(os_ops, OsOperations)
|
|
97
|
+
assert type(bin_dir) is str
|
|
98
|
+
assert type(data_dir) is str
|
|
99
|
+
assert type(__class__.C_BASH_EXE) is str
|
|
100
|
+
assert type(__class__.sm_exec_env) is dict
|
|
101
|
+
assert len(__class__.C_BASH_EXE) > 0
|
|
102
|
+
assert len(bin_dir) > 0
|
|
103
|
+
assert len(data_dir) > 0
|
|
104
|
+
|
|
105
|
+
pg_path_e = re.escape(os_ops.build_path(bin_dir, "postgres"))
|
|
106
|
+
data_dir_e = re.escape(data_dir)
|
|
107
|
+
|
|
108
|
+
assert type(pg_path_e) is str
|
|
109
|
+
assert type(data_dir_e) is str
|
|
110
|
+
|
|
111
|
+
regexp = r"^\s*[0-9]+\s+[0-9]+\s+" + pg_path_e + r"(\s+.*)?\s+\-[D]\s+" + data_dir_e + r"(\s+.*)?"
|
|
112
|
+
|
|
113
|
+
cmd = [
|
|
114
|
+
__class__.C_BASH_EXE,
|
|
115
|
+
"-c",
|
|
116
|
+
"ps -ewwo \"pid=,ppid=,args=\" | grep -E " + shlex.quote(regexp),
|
|
117
|
+
]
|
|
118
|
+
|
|
119
|
+
exec_r = os_ops.run(
|
|
120
|
+
cmd=cmd,
|
|
121
|
+
check=False,
|
|
122
|
+
exec_env=__class__.sm_exec_env,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
assert type(exec_r) is OsCommandResult
|
|
126
|
+
assert type(exec_r.returncode) is int
|
|
127
|
+
assert type(exec_r.stdout) is bytes
|
|
128
|
+
assert type(exec_r.stderr) is bytes
|
|
129
|
+
|
|
130
|
+
if exec_r.returncode == 1:
|
|
131
|
+
return None
|
|
132
|
+
|
|
133
|
+
output = exec_r.stdout.decode("utf-8")
|
|
134
|
+
error = exec_r.stderr.decode("utf-8")
|
|
135
|
+
|
|
136
|
+
assert type(output) is str
|
|
137
|
+
assert type(error) is str
|
|
138
|
+
|
|
139
|
+
if exec_r.returncode != 0:
|
|
140
|
+
errMsg = f"test command returned an unexpected exit code: {exec_r.returncode}"
|
|
141
|
+
raise ExecUtilException(
|
|
142
|
+
message=errMsg,
|
|
143
|
+
command=cmd,
|
|
144
|
+
exit_code=exec_r.returncode,
|
|
145
|
+
out=output,
|
|
146
|
+
error=error,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
lines = output.splitlines()
|
|
150
|
+
assert type(lines) is list
|
|
151
|
+
|
|
152
|
+
if len(lines) == 0:
|
|
153
|
+
# ACHTUNG!
|
|
154
|
+
raise RuntimeError("Command returns 0 error code without output.")
|
|
155
|
+
|
|
156
|
+
# parse result lines
|
|
157
|
+
pid_to_ppid: __class__.T_PID_TO_PPID = {}
|
|
158
|
+
|
|
159
|
+
for i_line in range(len(lines)):
|
|
160
|
+
assert type(lines[i_line]) is str
|
|
161
|
+
|
|
162
|
+
parts = lines[i_line].split()
|
|
163
|
+
assert type(parts) is list
|
|
164
|
+
|
|
165
|
+
if len(parts) < 2:
|
|
166
|
+
__class__._find_postmaster__throw_error__bad_line_format(
|
|
167
|
+
lines,
|
|
168
|
+
i_line,
|
|
169
|
+
"no usefull data",
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
if not parts[0].isdigit():
|
|
173
|
+
__class__._find_postmaster__throw_error__bad_line_format(
|
|
174
|
+
lines,
|
|
175
|
+
i_line,
|
|
176
|
+
"bad pid",
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
if not parts[1].isdigit():
|
|
180
|
+
__class__._find_postmaster__throw_error__bad_line_format(
|
|
181
|
+
lines,
|
|
182
|
+
i_line,
|
|
183
|
+
"bad ppid",
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
pid = int(parts[0])
|
|
187
|
+
ppid = int(parts[1])
|
|
188
|
+
|
|
189
|
+
if pid not in pid_to_ppid:
|
|
190
|
+
pid_to_ppid[pid] = ppid
|
|
191
|
+
continue
|
|
192
|
+
|
|
193
|
+
other_ppid = pid_to_ppid[pid]
|
|
194
|
+
assert type(other_ppid) is int
|
|
195
|
+
|
|
196
|
+
if ppid == other_ppid:
|
|
197
|
+
log_msg = "FindPostmaster (data_dir={!r}) get pid ({}) with ppid ({}) more than one time.".format(
|
|
198
|
+
data_dir,
|
|
199
|
+
pid,
|
|
200
|
+
ppid,
|
|
201
|
+
)
|
|
202
|
+
internal_utils.send_log_debug(log_msg)
|
|
203
|
+
continue
|
|
204
|
+
|
|
205
|
+
# ACTUNG ppid is changed --> restart
|
|
206
|
+
__class__._find_postmaster__throw_error__ppid_is_changed(
|
|
207
|
+
pid,
|
|
208
|
+
ppid,
|
|
209
|
+
other_ppid,
|
|
210
|
+
lines,
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
assert len(pid_to_ppid) <= len(lines)
|
|
214
|
+
|
|
215
|
+
true_postmasters = [
|
|
216
|
+
pid for pid, ppid in pid_to_ppid.items()
|
|
217
|
+
if ppid not in pid_to_ppid
|
|
218
|
+
]
|
|
219
|
+
|
|
220
|
+
if len(true_postmasters) == 0:
|
|
221
|
+
__class__._find_postmaster__throw_error__cycle(
|
|
222
|
+
pid_to_ppid,
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
if len(true_postmasters) == 1:
|
|
226
|
+
true_pid = true_postmasters[0]
|
|
227
|
+
|
|
228
|
+
if len(pid_to_ppid) > 1:
|
|
229
|
+
msg = "Many processes like a postmaster for data dir [{}] are found ({}).".format(
|
|
230
|
+
data_dir,
|
|
231
|
+
len(true_postmasters),
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
msg += " List (ppid->pid): {}.".format(
|
|
235
|
+
__class__._make_text_from_pid_to_ppid(pid_to_ppid),
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
msg += " True postmaster PID is {}.".format(true_pid)
|
|
239
|
+
internal_utils.send_log_debug(msg)
|
|
240
|
+
|
|
241
|
+
return true_pid
|
|
242
|
+
|
|
243
|
+
assert len(true_postmasters) > 1
|
|
244
|
+
|
|
245
|
+
__class__._find_postmaster__throw_error__many_postmasters(
|
|
246
|
+
true_postmasters,
|
|
247
|
+
pid_to_ppid,
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
# --------------------------------------------------------------------
|
|
251
|
+
def ProcessIsZombi_soft_check(
|
|
252
|
+
self,
|
|
253
|
+
os_ops: OsOperations,
|
|
254
|
+
pid: int,
|
|
255
|
+
) -> typing.Optional[bool]:
|
|
256
|
+
assert isinstance(os_ops, OsOperations)
|
|
257
|
+
assert type(pid) is int
|
|
258
|
+
|
|
259
|
+
# Change for macOS: Instead of reading non-existent /proc/PID/stat,
|
|
260
|
+
# we make a native call to the system ps and request the status (state) of the process.
|
|
261
|
+
cmd = ["ps", "-p", str(pid), "-wwo", "state="]
|
|
262
|
+
|
|
263
|
+
exec_r = os_ops.run(
|
|
264
|
+
cmd=cmd,
|
|
265
|
+
check=False,
|
|
266
|
+
exec_env=__class__.sm_exec_env,
|
|
267
|
+
stdout=subprocess.PIPE,
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
assert type(exec_r) is OsCommandResult
|
|
271
|
+
assert type(exec_r.stdout) is bytes
|
|
272
|
+
|
|
273
|
+
# If the process is not found (already terminated and deleted), ps will return code 1
|
|
274
|
+
if exec_r.returncode == 1:
|
|
275
|
+
return False
|
|
276
|
+
|
|
277
|
+
if exec_r.returncode != 0:
|
|
278
|
+
return None
|
|
279
|
+
|
|
280
|
+
proc_status = exec_r.stdout.decode("utf-8").rstrip()
|
|
281
|
+
|
|
282
|
+
if not proc_status:
|
|
283
|
+
return None
|
|
284
|
+
|
|
285
|
+
assert len(proc_status) > 0
|
|
286
|
+
|
|
287
|
+
# state The state is given by a sequence of letters, for example,
|
|
288
|
+
# "RWNA". The first letter indicates the run state of the process:
|
|
289
|
+
#
|
|
290
|
+
# D Marks a process in disk (or other short term, uninterruptible) wait.[legacy option]
|
|
291
|
+
# I Marks a process that is idle (sleeping for longer than about 20 seconds).
|
|
292
|
+
# R Marks a runnable process.
|
|
293
|
+
# S Marks a process that is sleeping for less than about 20 seconds.
|
|
294
|
+
# T Marks a stopped process.
|
|
295
|
+
# U Marks a process in uninterruptible wait.
|
|
296
|
+
# Z Marks a dead process (a 'zombie').
|
|
297
|
+
#
|
|
298
|
+
# Additional characters after these, if any, indicate additional
|
|
299
|
+
# state information:
|
|
300
|
+
#
|
|
301
|
+
# + The process is in the foreground process group of its control terminal.
|
|
302
|
+
# < The process has raised CPU scheduling priority.
|
|
303
|
+
# > The process has specified a soft limit on memory requirements and is currently
|
|
304
|
+
# exceeding that limit; such a process is (necessarily) not swapped.
|
|
305
|
+
# A the process has asked for random page replacement
|
|
306
|
+
# (VA_ANOM, from vadvise(2), for example, lisp(1) in a garbage collect).
|
|
307
|
+
# E The process is trying to exit.
|
|
308
|
+
# L The process has pages locked in core (for example, for raw I/O).
|
|
309
|
+
# N The process has reduced CPU scheduling priority (see setpriority(2)).
|
|
310
|
+
# S The process has asked for FIFO page replacement (VA_SEQL,
|
|
311
|
+
# from vadvise(2), for example, a large image processing
|
|
312
|
+
# program using virtual memory to sequentially address
|
|
313
|
+
# voluminous data).
|
|
314
|
+
# s The process is a session leader.
|
|
315
|
+
# V The process is suspended during a vfork.
|
|
316
|
+
# W The process is swapped out.
|
|
317
|
+
# X The process is being traced or debugged.
|
|
318
|
+
|
|
319
|
+
ch1 = proc_status[0]
|
|
320
|
+
|
|
321
|
+
if ch1 == "Z":
|
|
322
|
+
return True
|
|
323
|
+
|
|
324
|
+
if ch1 in "DIRSTU":
|
|
325
|
+
return False
|
|
326
|
+
|
|
327
|
+
return None
|
|
328
|
+
|
|
329
|
+
T_PID_TO_PPID = typing.Dict[int, int]
|
|
330
|
+
|
|
331
|
+
@staticmethod
|
|
332
|
+
def _make_text_from_pid_to_ppid(pid_to_ppid: T_PID_TO_PPID) -> str:
|
|
333
|
+
result = ""
|
|
334
|
+
sep = ""
|
|
335
|
+
for pid, ppid in pid_to_ppid.items():
|
|
336
|
+
result += sep + " {}->{}".format(ppid, pid)
|
|
337
|
+
sep = ", "
|
|
338
|
+
continue
|
|
339
|
+
return result
|
|
340
|
+
|
|
341
|
+
@staticmethod
|
|
342
|
+
def _find_postmaster__throw_error__bad_line_format(
|
|
343
|
+
lines: typing.List[str],
|
|
344
|
+
i_line: int,
|
|
345
|
+
hint: str,
|
|
346
|
+
) -> typing.NoReturn:
|
|
347
|
+
assert type(lines) is list
|
|
348
|
+
assert type(i_line) is int
|
|
349
|
+
assert type(hint) is str
|
|
350
|
+
|
|
351
|
+
error_lines: typing.List[str] = []
|
|
352
|
+
error_lines.append(
|
|
353
|
+
"Line {} has bad format. Hint: {}.".format(
|
|
354
|
+
i_line + 1,
|
|
355
|
+
hint,
|
|
356
|
+
),
|
|
357
|
+
)
|
|
358
|
+
error_lines.append(
|
|
359
|
+
"Problem line is:"
|
|
360
|
+
)
|
|
361
|
+
error_lines.append(
|
|
362
|
+
" " + repr(lines[i_line]),
|
|
363
|
+
)
|
|
364
|
+
error_lines.append(
|
|
365
|
+
"All the lines is:",
|
|
366
|
+
)
|
|
367
|
+
for i in range(len(lines)):
|
|
368
|
+
error_lines.append(
|
|
369
|
+
" {}. {!r}".format(i + 1, lines[i]),
|
|
370
|
+
)
|
|
371
|
+
continue
|
|
372
|
+
|
|
373
|
+
raise RuntimeError("\n".join(error_lines))
|
|
374
|
+
|
|
375
|
+
@staticmethod
|
|
376
|
+
def _find_postmaster__throw_error__ppid_is_changed(
|
|
377
|
+
pid: int,
|
|
378
|
+
ppid: int,
|
|
379
|
+
other_ppid: int,
|
|
380
|
+
lines: typing.List[str],
|
|
381
|
+
) -> typing.NoReturn:
|
|
382
|
+
assert type(pid) is int
|
|
383
|
+
assert type(ppid) is int
|
|
384
|
+
assert type(other_ppid) is int
|
|
385
|
+
assert type(lines) is list
|
|
386
|
+
|
|
387
|
+
error_lines: typing.List[str] = []
|
|
388
|
+
error_lines.append(
|
|
389
|
+
"Parent of process ({}) is changed from {} to {}.".format(
|
|
390
|
+
pid,
|
|
391
|
+
other_ppid,
|
|
392
|
+
ppid,
|
|
393
|
+
),
|
|
394
|
+
)
|
|
395
|
+
error_lines.append(
|
|
396
|
+
"All the lines is:",
|
|
397
|
+
)
|
|
398
|
+
for i in range(len(lines)):
|
|
399
|
+
error_lines.append(
|
|
400
|
+
" {}. {!r}".format(i + 1, lines[i]),
|
|
401
|
+
)
|
|
402
|
+
continue
|
|
403
|
+
|
|
404
|
+
raise RuntimeError("\n".join(error_lines))
|
|
405
|
+
|
|
406
|
+
@staticmethod
|
|
407
|
+
def _find_postmaster__throw_error__cycle(
|
|
408
|
+
pid_to_ppid: T_PID_TO_PPID,
|
|
409
|
+
) -> typing.NoReturn:
|
|
410
|
+
msg = "Cycle in processes postgres process tree. "
|
|
411
|
+
|
|
412
|
+
msg += " List (ppid->pid): {},".format(
|
|
413
|
+
__class__._make_text_from_pid_to_ppid(pid_to_ppid),
|
|
414
|
+
)
|
|
415
|
+
|
|
416
|
+
msg += " List size is {}.".format(
|
|
417
|
+
len(pid_to_ppid),
|
|
418
|
+
)
|
|
419
|
+
raise RuntimeError(msg)
|
|
420
|
+
|
|
421
|
+
@staticmethod
|
|
422
|
+
def _find_postmaster__throw_error__many_postmasters(
|
|
423
|
+
postmaster_pids: typing.List[int],
|
|
424
|
+
pid_to_ppid: T_PID_TO_PPID,
|
|
425
|
+
) -> typing.NoReturn:
|
|
426
|
+
msg = "Many processes like a postmaster are found ({}): {}.".format(
|
|
427
|
+
len(postmaster_pids),
|
|
428
|
+
", ".join(map(str, postmaster_pids)),
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
msg += " Trees (ppid->pid): {}.".format(
|
|
432
|
+
__class__._make_text_from_pid_to_ppid(pid_to_ppid),
|
|
433
|
+
)
|
|
434
|
+
|
|
435
|
+
msg += " Total process count is {}.".format(len(pid_to_ppid))
|
|
436
|
+
raise RuntimeError(msg)
|
|
437
|
+
|
|
438
|
+
@staticmethod
|
|
439
|
+
def _find_postmaster__throw_error__fail(
|
|
440
|
+
bin_dir: str,
|
|
441
|
+
data_dir: str,
|
|
442
|
+
failures: typing.List[Exception],
|
|
443
|
+
) -> typing.NoReturn:
|
|
444
|
+
assert type(bin_dir) is str
|
|
445
|
+
assert type(data_dir) is str
|
|
446
|
+
assert type(failures) is list
|
|
447
|
+
|
|
448
|
+
method_name = "InternalPlatformUtils::FindPostmaster(bin_dir={!r}, data_dir={!r})".format(
|
|
449
|
+
bin_dir,
|
|
450
|
+
data_dir,
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
RaiseError.function_did_multiple_attempts_without_stable_result(
|
|
454
|
+
method_name,
|
|
455
|
+
failures,
|
|
456
|
+
)
|
|
@@ -19,5 +19,9 @@ def create_internal_platform_utils(
|
|
|
19
19
|
from .win32 import internal_platform_utils as x
|
|
20
20
|
return x.InternalPlatformUtils()
|
|
21
21
|
|
|
22
|
+
if platform_name == "darwin":
|
|
23
|
+
from .darwin import internal_platform_utils as x
|
|
24
|
+
return x.InternalPlatformUtils()
|
|
25
|
+
|
|
22
26
|
# not implemented
|
|
23
27
|
return InternalPlatformUtils()
|
|
@@ -113,7 +113,7 @@ class InternalPlatformUtils(base.InternalPlatformUtils):
|
|
|
113
113
|
__class__.C_BASH_EXE,
|
|
114
114
|
"-c",
|
|
115
115
|
"ps -ewwo \"pid=,ppid=,args=\" | grep -E " + shlex.quote(regexp),
|
|
116
|
-
|
|
116
|
+
]
|
|
117
117
|
|
|
118
118
|
exec_r = os_ops.run(
|
|
119
119
|
cmd=cmd,
|
|
@@ -246,6 +246,7 @@ class InternalPlatformUtils(base.InternalPlatformUtils):
|
|
|
246
246
|
pid_to_ppid,
|
|
247
247
|
)
|
|
248
248
|
|
|
249
|
+
# --------------------------------------------------------------------
|
|
249
250
|
def ProcessIsZombi_soft_check(
|
|
250
251
|
self,
|
|
251
252
|
os_ops: OsOperations,
|
|
@@ -316,7 +317,7 @@ class InternalPlatformUtils(base.InternalPlatformUtils):
|
|
|
316
317
|
) -> typing.NoReturn:
|
|
317
318
|
assert type(lines) is list
|
|
318
319
|
assert type(i_line) is int
|
|
319
|
-
assert type(hint) is
|
|
320
|
+
assert type(hint) is str
|
|
320
321
|
|
|
321
322
|
error_lines: typing.List[str] = []
|
|
322
323
|
error_lines.append(
|