Fix segfault when ALWAYS_PRIMARY is set but DISALLOW_TO_FAILOVER.
authorTatsuo Ishii <ishii@sraoss.co.jp>
Tue, 15 Dec 2020 10:38:04 +0000 (19:38 +0900)
committerTatsuo Ishii <ishii@sraoss.co.jp>
Tue, 15 Dec 2020 10:54:27 +0000 (19:54 +0900)
If a primary node (for example node 0) has ALWAYS_PRIMARY but does
have DISALLOW_TO_FAILOVER, segfault happens in following step:

1) node 0 stops

2) failover happens. all children restart. The primary node is still 0
because ALWAYS_PRIMARY is set.

2) node 0 starts and it becomes online by using pcp_attach_node.

3) failback process is executed but child process do not restart.

4) child process accesses node 0 because Req_info->primary_node_id is
0 but connection to node 0 does not exist and this causes segfault.

To fix this, do additional check to make sure that failback node is
not former primary node. If it is, do full restart of child process.

Per bug 672.

src/main/pgpool_main.c

index c8cc771e3a0ece66aff3e48c95be26569fc7c086..e1edf948c0353b34ae060d290f8f0d4ab7624d11 100644 (file)
@@ -1864,11 +1864,18 @@ static void failover(void)
                 * went down, restarted, re-attached without promotion. Then existing
                 * child process loses connection slot to node 0 and keeps on using it
                 * when node 0 comes back. This could result in segfault later on in
-                * the child process because there's no connection to node id 0.  See
-                * bug 672 for more details.
+                * the child process because there's no connection to node id 0.
+                *
+                * Actually we need to think about when ALWAYS_PRIMARY flag is set
+                * *but* DISALLOW_TO_FAILOVER flag is not set case. In the case after
+                * primary failover Req_info->primary_node_id is set, but connection
+                * to the primary node does not exist. So we should do full restart if
+                * requested node id is the former primary node.
+                *
+                * See bug 672 for more details.
                 */
                if (STREAM && reqkind == NODE_UP_REQUEST && all_backend_down == false &&
-                       Req_info->primary_node_id >= 0)
+                       Req_info->primary_node_id >= 0 && Req_info->primary_node_id != node_id)
                {
                        ereport(LOG,
                                        (errmsg("Do not restart children because we are failing back node id %d host: %s port: %d and we are in streaming replication mode and not all backends were down", node_id,