Skip to content

Instantly share code, notes, and snippets.

@QQGoblin
Last active December 23, 2021 09:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save QQGoblin/138b9860d2484fd2e162dd56dfb4e580 to your computer and use it in GitHub Desktop.
Save QQGoblin/138b9860d2484fd2e162dd56dfb4e580 to your computer and use it in GitHub Desktop.
【Patroni源码阅读】Postgresql 停止PG
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