postmaster只充当一个中介的角色,不过多地涉及共享内存和其他会引起错误的操作,使得postmaster主程序更健壮和稳定。同时如果在ServerLoop里面花时间做验证我觉得也太费时间了。
哦,忘了,我们还没说client的连接请求的认证。下面是函数调用栈:
BackendRun() |->PostgresMain() |->InitPostgres() |->PerformAuthentication() |->ClientAuthentication()PerformAuthentication()在InitPostgres中是在EnablePortalManager()之后调用的,这个时候大部分backend进程本身的初始化工作都已完毕。
这里做Client验证的入口是ClientAuthentication()了。它的主要工作如下:
hba_getauthmethod() //获取hba文件中和该条请求匹配的auth method | v switch(auth_method)//根据auth_method和请求信息做出相应的处理 | v status == STATUS_OK ? sendAuthRequest() : auth_failed() //根据返回值决定向client发送验证packet还是拒绝请求需要说明的是当验证失败,拒绝client的请求后,程序在这里就报错退出了。这样,这个backend就是一个dead_end的backend,他会在postmaster的指挥下退出,具体细节见后面几节内容。
2.2客户端取消查询时的中介当我们在client(例如psql命令行)中运行一个很长的SQL查询(并不是说一定要很长的查询,只是如果时间太短的话你根本来不及cancel~)时,此时由于各种原因你想中止这条查询,于是你按下了Crtl+C键。立即在客户端上显示:
postgres=# select * from test order by id asc ; Cancel request sent ERROR: canceling statement due to user request那我们来看一看postgres是如何处理这样的cancel吧。
先上图:
对应上图,我们针对涉及的进程分别列出函数调用栈:
client:
psql命令在初始化的时候调用setup_cancel_handler()在psql的MainLoop之前注册了一个信号处理函数,在收到client的SIGINT(也就是你按下Ctrl+C)后,调用handle_sigint()处理这个信号。处理成功后,打印:
Cancel request sent
(src/bin/psql/startup.c) main() |->setup_cancel_handler() |->pqsignal(SIGINT, handle_sigint) |->successResult = MainLoop(stdin)postmaster:processCancelRequest: postmaster在接收到client发来的packet后,建立一个后端进程(backend)去处理它,当发现它是一个cancel_request_packet后,调用processCancelRequest()函数处理这个packet,通过PID向对应的backend发送SIGINT信号。
PostmasterMain() |->ServerLoop() |->for(;;) |->BackendStartup() <--建立后端进程backend process |->BackendInitialize() |->ProcessStartupPacket() |->processCancelRequest() |->signal_child(bp->pid, SIGINT)backend:pqsignal(SIGINT, StatementCancelHandler): postgresMain()函数上注册下面这个信号处理函数,它接受postmaster发来的SIGINT信号,进行对应的处理,设置两个全局变量:
pqsignal(SIGINT, StatementCancelHandler) { ... InterruptPending = true; QueryCancelPending = true; ... }而这两个全局变量又决定了CHECK_FOR_INTERRUPTS()是否生效:
#define CHECK_FOR_INTERRUPTS() \ do { \ if (InterruptPending) \ ProcessInterrupts(); \ } while(0)我们进ProcessInterrupts()函数,发现他就是用来处理client的中止请求的:
ProcessInterrupts(){ ... InterruptPending = false; ... { LockErrorCleanup(); ereport(ERROR, (errcode(ERRCODE_QUERY_CANCELED), errmsg("canceling statement due to user request"))); } ... }报错消息就是我们上面所见的那条了。
2.3接受pg_ctl的shutdown请求我们经常会使用pg_ctl 来控制postgres服务器,比如start,stop和reload等等。start参数就是对应着服务器的启动,这个在Postgres中postmaster代码解析(上)中我们已经讨论过。这里我们来讨论下指定stop参数的处理。
在开始讨论之前,我们先看下PMState这个枚举类型。
typedef enum { PM_INIT, /* postmaster starting */ PM_STARTUP, /* waiting for startup subprocess */ PM_RECOVERY, /* in archive recovery mode */ PM_HOT_STANDBY, /* in hot standby mode */ PM_RUN, /* normal "database is alive" state */ PM_WAIT_BACKUP, /* waiting for online backup mode to end */ PM_WAIT_READONLY, /* waiting for read only backends to exit */ PM_WAIT_BACKENDS, /* waiting for live backends to exit */ PM_SHUTDOWN, /* waiting for checkpointer to do shutdown * ckpt */ PM_SHUTDOWN_2, /* waiting for archiver and walsenders to * finish */ PM_WAIT_DEAD_END, /* waiting for dead_end children to exit */ PM_NO_CHILDREN /* all important children have exited */ } PMState;