Last active
December 23, 2021 09:34
-
-
Save QQGoblin/138b9860d2484fd2e162dd56dfb4e580 to your computer and use it in GitHub Desktop.
【Patroni源码阅读】Postgresql 停止PG
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Postgresql(object): | |
def stop(self, mode='fast', block_callbacks=False, checkpoint=None, | |
on_safepoint=None, on_shutdown=None, stop_timeout=None): | |
"""Stop PostgreSQL | |
Supports a callback when a safepoint is reached. A safepoint is when no user backend can return a successful | |
commit to users. Currently this means we wait for user backends to close. But in the future alternate mechanisms | |
could be added. | |
:param on_safepoint: This callback is called when no user backends are running. | |
:param on_shutdown: is called when pg_controldata starts reporting `Database cluster state: shut down` | |
""" | |
if checkpoint is None: | |
checkpoint = False if mode == 'immediate' else True | |
success, pg_signaled = self._do_stop(mode, block_callbacks, checkpoint, on_safepoint, on_shutdown, stop_timeout) | |
if success: | |
# block_callbacks is used during restart to avoid | |
# running start/stop callbacks in addition to restart ones | |
if not block_callbacks: | |
self.set_state('stopped') | |
if pg_signaled: | |
self.call_nowait(ACTION_ON_STOP) | |
else: | |
logger.warning('pg_ctl stop failed') | |
self.set_state('stop failed') | |
return success | |
def _do_stop(self, mode, block_callbacks, checkpoint, on_safepoint, on_shutdown, stop_timeout): | |
# self.is_running() 方法会通过 postmaster.pid 文件返回 postgres 进程 | |
# postmaster.pid 是有格式信息的,每一行的内容为: ['pid', 'data_dir', 'start_time', 'port', 'socket_dir', 'listen_addr', 'shmem_key'] | |
postmaster = self.is_running() | |
if not postmaster: | |
if on_safepoint: | |
# 调用on_safepoint回调,此时所有user进程已经退出(PS:因为postmaster进程都不在了) | |
on_safepoint() | |
return True, False | |
if checkpoint and not self.is_starting(): | |
# checkpoint用来保证数据一致性和完整性,调用sql执行CHECKPOINT | |
# PS:只有Master节点才执行,SELECT pg_catalog.pg_is_in_recovery() 为 t 时不执行 | |
self.checkpoint(timeout=stop_timeout) | |
if not block_callbacks: | |
self.set_state('stopping') | |
# Send signal to postmaster to stop | |
# 发送signal停止postgres进程 | |
success = postmaster.signal_stop(mode, self.pgcommand('pg_ctl')) | |
if success is not None: | |
if success and on_safepoint: | |
# pg进程不存在时调用on_safepoint | |
# ps:postmaster.signal_stop是异步的,正常是返回None | |
on_safepoint() | |
return success, True | |
# We can skip safepoint detection if we don't have a callback | |
if on_safepoint: | |
# Wait for our connection to terminate so we can be sure that no new connections are being initiated | |
# 反复执行 SELECT 1 确认 pg 已经不接受用户请求 | |
self._wait_for_connection_close(postmaster) | |
# 确认相关进程已经终止 | |
postmaster.wait_for_user_backends_to_close() | |
on_safepoint() | |
if on_shutdown and mode in ('fast', 'smart'): | |
i = 0 | |
# Wait for pg_controldata `Database cluster state:` to change to "shut down" | |
# pg_controldata 状态已经是shut down,但是postgres主进程没有退出 | |
while postmaster.is_running(): | |
data = self.controldata() | |
if data.get('Database cluster state', '') == 'shut down': | |
on_shutdown(int(self.latest_checkpoint_location())) | |
break | |
elif data.get('Database cluster state', '').startswith('shut down'): # shut down in recovery | |
break | |
elif stop_timeout and i >= stop_timeout: | |
stop_timeout = 0 | |
break | |
time.sleep(STOP_POLLING_INTERVAL) | |
i += STOP_POLLING_INTERVAL | |
try: | |
postmaster.wait(timeout=stop_timeout) | |
except TimeoutExpired: | |
logger.warning("Timeout during postmaster stop, aborting Postgres.") | |
# 停止超时,强制终止PG | |
if not self.terminate_postmaster(postmaster, mode, stop_timeout): | |
postmaster.wait() | |
return True, True |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment