Add pcp_healt_check_stats command.
authorTatsuo Ishii <ishii@sraoss.co.jp>
Mon, 24 Feb 2020 13:29:24 +0000 (22:29 +0900)
committerTatsuo Ishii <ishii@sraoss.co.jp>
Mon, 24 Feb 2020 13:34:42 +0000 (22:34 +0900)
16 files changed:
doc.ja/src/sgml/ref/allfiles.sgml
doc.ja/src/sgml/ref/pcp_health_check_stats.sgml [new file with mode: 0644]
doc.ja/src/sgml/reference.sgml
doc/src/sgml/ref/allfiles.sgml
doc/src/sgml/ref/pcp_health_check_stats.sgml [new file with mode: 0644]
doc/src/sgml/reference.sgml
src/Makefile.am
src/include/pcp/libpcp_ext.h
src/libs/pcp/Makefile.am
src/libs/pcp/pcp.c
src/pcp_con/README.add_pcp_command [new file with mode: 0644]
src/pcp_con/pcp_worker.c
src/tools/pcp/Makefile.am
src/tools/pcp/pcp_frontend_client.c
src/utils/pool_health_check_stats.c [new file with mode: 0644]
src/utils/pool_process_reporting.c

index 7a643ccaf51442ecfd51ca940ce8049fd4ab2003..195ae28db19b9c095e99b7a8473359eb788e2d26 100644 (file)
@@ -7,6 +7,7 @@ Complete list of usable sgml source files in this directory.
 <!ENTITY pcpCommonOptions    SYSTEM "pcp_common_options.sgml">
 <!ENTITY pcpNodeCount        SYSTEM "pcp_node_count.sgml">
 <!ENTITY pcpNodeInfo         SYSTEM "pcp_node_info.sgml">
+<!ENTITY pcpHealthCheckStats SYSTEM "pcp_health_check_stats.sgml">
 <!ENTITY pcpWatchdogInfo     SYSTEM "pcp_watchdog_info.sgml">
 <!ENTITY pcpProcCount        SYSTEM "pcp_proc_count.sgml">
 <!ENTITY pcpProcInfo         SYSTEM "pcp_proc_info.sgml">
diff --git a/doc.ja/src/sgml/ref/pcp_health_check_stats.sgml b/doc.ja/src/sgml/ref/pcp_health_check_stats.sgml
new file mode 100644 (file)
index 0000000..2a9deea
--- /dev/null
@@ -0,0 +1,103 @@
+<!--
+doc/src/sgml/ref/pcp_health_check_stats.sgml
+Pgpool-II documentation
+-->
+
+<refentry id="PCP-HEALTH-CHECK-STATS">
+ <indexterm zone="pcp-health-check-stats">
+  <primary>pcp_health_check_stats</primary>
+ </indexterm>
+
+ <refmeta>
+  <refentrytitle>pcp_health_check_stats</refentrytitle>
+  <manvolnum>1</manvolnum>
+  <refmiscinfo>PCP Command</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>pcp_health_check_stats</refname>
+  <refpurpose>
+   与えられたノードIDのヘルスチェック統計データを表示する
+  </refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+  <cmdsynopsis>
+   <command>pcp_health_check_stats</command>
+   <arg rep="repeat"><replaceable>option</replaceable></arg>
+   <arg><replaceable>node_id</replaceable></arg>
+  </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1 id="R1-PCP-HEALTH-CHECK-STATS-1">
+  <title>Description</title>
+  <para>
+   <command>pcp_health_check_stats</command>
+   与えられたノードIDのヘルスチェック統計データを表示します。
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>オプション</title>
+  <para>
+   <variablelist>
+
+    <varlistentry>
+     <term><option>-n <replaceable class="parameter">node_id</replaceable></option></term>
+     <term><option>--node-id=<replaceable class="parameter">node_id</replaceable></option></term>
+     <listitem>
+      <para>
+       情報を表示するバックエンドノードのインデックスを指定します。
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
+     <term><option>その他のオプション</option></term>
+     <listitem>
+      <para>
+       <xref linkend="pcp-common-options"> を参照してください。
+      </para>
+     </listitem>
+    </varlistentry>
+   </variablelist>
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>例</title>
+  <para>
+   例を示します。
+   <programlisting>
+$ pcp_health_check_stats -h localhost -p 11001 -w 0
+0 /tmp 11002 up primary 2020-02-24 22:02:42 3 3 0 0 0 0.000000 0 5 1 3.666667 2020-02-24 22:02:47 2020-02-24 22:02:47  
+$ pcp_health_check_stats -h localhost -p 11001 -w -v 0
+Node Id                       : 0
+Host Name                     : /tmp
+Port                          : 11002
+Status                        : up
+Role                          : primary
+Last Status Change            : 2020-02-24 22:02:42
+Total Count                   : 5
+Success Count                 : 5
+Fail Count                    : 0
+Skip Count                    : 0
+Retry Count                   : 0
+Average Retry Count           : 0.000000
+Max Retry Count               : 0
+Max Health Check Duration     : 5
+Minimum Health Check Duration : 1
+Average Health Check Duration : 4.200000
+Last Health Check             : 2020-02-24 22:03:07
+Last Successful Health Check  : 2020-02-24 22:03:07
+Last Skip Health Check        : 
+Last Failed Health Check      : 
+   </programlisting>
+  </para>
+
+  <para>
+   表示データの詳細に関しては<xref linkend="health-check-stats-data-table">をご覧ください。
+  </para>
+ </refsect1>
+
+</refentry>
index 75982801b43b140942bcefef16c9d1b952388bdb..2393fef0f5e2daf511c15133040681a67d64a00c 100644 (file)
   &pcpCommonOptions;
   &pcpNodeCount;
   &pcpNodeInfo;
+  &pcpHealthCheckStats;
   &pcpWatchdogInfo;
   &pcpProcCount;
   &pcpProcInfo;
index 7a643ccaf51442ecfd51ca940ce8049fd4ab2003..195ae28db19b9c095e99b7a8473359eb788e2d26 100644 (file)
@@ -7,6 +7,7 @@ Complete list of usable sgml source files in this directory.
 <!ENTITY pcpCommonOptions    SYSTEM "pcp_common_options.sgml">
 <!ENTITY pcpNodeCount        SYSTEM "pcp_node_count.sgml">
 <!ENTITY pcpNodeInfo         SYSTEM "pcp_node_info.sgml">
+<!ENTITY pcpHealthCheckStats SYSTEM "pcp_health_check_stats.sgml">
 <!ENTITY pcpWatchdogInfo     SYSTEM "pcp_watchdog_info.sgml">
 <!ENTITY pcpProcCount        SYSTEM "pcp_proc_count.sgml">
 <!ENTITY pcpProcInfo         SYSTEM "pcp_proc_info.sgml">
diff --git a/doc/src/sgml/ref/pcp_health_check_stats.sgml b/doc/src/sgml/ref/pcp_health_check_stats.sgml
new file mode 100644 (file)
index 0000000..824b634
--- /dev/null
@@ -0,0 +1,102 @@
+<!--
+doc/src/sgml/ref/pcp_health_check_stats.sgml
+Pgpool-II documentation
+-->
+
+<refentry id="PCP-HEALTH-CHECK-STATS">
+ <indexterm zone="pcp-health-check-stats">
+  <primary>pcp_health_check_stats</primary>
+ </indexterm>
+
+ <refmeta>
+  <refentrytitle>pcp_health_check_stats</refentrytitle>
+  <manvolnum>1</manvolnum>
+  <refmiscinfo>PCP Command</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>pcp_health_check_stats</refname>
+  <refpurpose>
+   displays health check statistics data on given node ID</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+  <cmdsynopsis>
+   <command>pcp_health_check_stats</command>
+   <arg rep="repeat"><replaceable>option</replaceable></arg>
+   <arg><replaceable>node_id</replaceable></arg>
+  </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1 id="R1-PCP-HEALTH-CHECK-STATS-1">
+  <title>Description</title>
+  <para>
+   <command>pcp_health_check_stats</command>
+   displays health check statistics data on given node ID.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Options</title>
+  <para>
+   <variablelist>
+
+    <varlistentry>
+     <term><option>-n <replaceable class="parameter">node_id</replaceable></option></term>
+     <term><option>--node-id=<replaceable class="parameter">node_id</replaceable></option></term>
+     <listitem>
+      <para>
+       The index of backend node to get information of.
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
+     <term><option>Other options </option></term>
+     <listitem>
+      <para>
+       See <xref linkend="pcp-common-options">.
+      </para>
+     </listitem>
+    </varlistentry>
+   </variablelist>
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Example</title>
+  <para>
+   Here is an example output:
+   <programlisting>
+$ pcp_health_check_stats -h localhost -p 11001 -w 0
+0 /tmp 11002 up primary 2020-02-24 22:02:42 3 3 0 0 0 0.000000 0 5 1 3.666667 2020-02-24 22:02:47 2020-02-24 22:02:47  
+$ pcp_health_check_stats -h localhost -p 11001 -w -v 0
+Node Id                       : 0
+Host Name                     : /tmp
+Port                          : 11002
+Status                        : up
+Role                          : primary
+Last Status Change            : 2020-02-24 22:02:42
+Total Count                   : 5
+Success Count                 : 5
+Fail Count                    : 0
+Skip Count                    : 0
+Retry Count                   : 0
+Average Retry Count           : 0.000000
+Max Retry Count               : 0
+Max Health Check Duration     : 5
+Minimum Health Check Duration : 1
+Average Health Check Duration : 4.200000
+Last Health Check             : 2020-02-24 22:03:07
+Last Successful Health Check  : 2020-02-24 22:03:07
+Last Skip Health Check        : 
+Last Failed Health Check      : 
+   </programlisting>
+  </para>
+
+  <para>
+   See <xref linkend="health-check-stats-data-table"> for details of data.
+  </para>
+ </refsect1>
+
+</refentry>
index 17cb8843a0f6ee21a4db072772842c91cf07a090..5ba2892fb1c1b635b64849ce30967faf6f8cc92c 100644 (file)
@@ -95,6 +95,7 @@
   &pcpCommonOptions;
   &pcpNodeCount;
   &pcpNodeInfo;
+  &pcpHealthCheckStats;
   &pcpWatchdogInfo;
   &pcpProcCount;
   &pcpProcInfo;
index 34e95b4a16624f0207d43d87742ad7f0b140610e..8edb820a20131cee083c869ccd4e4ea030e83da9 100644 (file)
@@ -59,7 +59,8 @@ pgpool_SOURCES = main/main.c \
        utils/base64.c \
        utils/sha2.c \
        utils/ssl_utils.c \
-    utils/statistics.c
+       utils/statistics.c \
+       utils/pool_health_check_stats.c
 
 DEFS = @DEFS@ \
        -DDEFAULT_CONFIGDIR=\"$(sysconfdir)\" \
index 8b3a91ab16960ec1ac79060e0b9da0c6abeb2db5..caf05bbdc269ed0fb19edc71fcf0c5a253359f33 100644 (file)
@@ -316,7 +316,7 @@ extern void pcp_disconnect(PCPConnInfo * pcpConn);
 extern PCPResultInfo * pcp_terminate_pgpool(PCPConnInfo * pcpCon, char mode);
 extern PCPResultInfo * pcp_node_count(PCPConnInfo * pcpCon);
 extern PCPResultInfo * pcp_node_info(PCPConnInfo * pcpCon, int nid);
-
+extern PCPResultInfo * pcp_health_check_stats(PCPConnInfo * pcpCon, int nid);
 extern PCPResultInfo * pcp_process_count(PCPConnInfo * pcpConn);
 extern PCPResultInfo * pcp_process_info(PCPConnInfo * pcpConn, int pid);
 
@@ -345,6 +345,8 @@ extern int  pcp_result_is_empty(PCPResultInfo * res);
 
 extern char *role_to_str(SERVER_ROLE role);
 
+extern int * pool_health_check_stats_offsets(int *n);
+
 /* ------------------------------
  * pcp_error.c
  * ------------------------------
index ccd9115bb2d8f2dd686578200ddc850716d5c7a4..feecdf49c419fc90ed9e1ea5b0284047ecca7106 100644 (file)
@@ -6,7 +6,8 @@ dist_libpcp_la_SOURCES = pcp.c \
                                        ../../utils/pool_path.c \
                                        ../../tools/fe_port.c \
                                        ../../tools/fe_memutils.c \
-                                       ../../utils/strlcpy.c
+                                       ../../utils/strlcpy.c \
+                                       ../../utils/pool_health_check_stats.c
 nodist_libpcp_la_SOURCES = pcp_stream.c \
                                        md5.c \
                                        json.c
index 8a982ad409ab389e9b4f6de3b363d893da85ca50..788d7224a25cbed71082b9bdc52076958b19c22e 100644 (file)
@@ -8,7 +8,7 @@
  * pgpool: a language independent connection pool server for PostgreSQL
  * written by Tatsuo Ishii
  *
- * Copyright (c) 2003-2019     PgPool Global Development Group
+ * Copyright (c) 2003-2020     PgPool Global Development Group
  *
  * Permission to use, copy, modify, and distribute this software and
  * its documentation for any purpose and without fee is hereby
@@ -65,6 +65,7 @@ static int    setNextResultBinaryData(PCPResultInfo * res, void *value, int datalen
 static void setResultIntData(PCPResultInfo * res, unsigned int slotno, int value);
 
 static void process_node_info_response(PCPConnInfo * pcpConn, char *buf, int len);
+static void    process_health_check_stats_response(PCPConnInfo * pcpConn, char *buf, int len);
 static void process_command_complete_response(PCPConnInfo * pcpConn, char *buf, int len);
 static void process_watchdog_info_response(PCPConnInfo * pcpConn, char *buf, int len);
 static void process_process_info_response(PCPConnInfo * pcpConn, char *buf, int len);
@@ -365,6 +366,7 @@ static PCPResultInfo * process_pcp_response(PCPConnInfo * pcpConn, char sentMsg)
                        setResultStatus(pcpConn, PCP_RES_ERROR);
                        return pcpConn->pcpResInfo;
                }
+
                if (pcp_read(pcpConn->pcpConn, &rsize, sizeof(int)))
                {
                        pcp_internal_error(pcpConn,
@@ -433,6 +435,13 @@ static PCPResultInfo * process_pcp_response(PCPConnInfo * pcpConn, char sentMsg)
                                        process_node_info_response(pcpConn, buf, rsize);
                                break;
 
+                       case 'h':
+                               if (sentMsg != 'H')
+                                       setResultStatus(pcpConn, PCP_RES_BAD_RESPONSE);
+                               else
+                                       process_health_check_stats_response(pcpConn, buf, rsize);
+                               break;
+
                        case 'l':
                                if (sentMsg != 'L')
                                        setResultStatus(pcpConn, PCP_RES_BAD_RESPONSE);
@@ -807,6 +816,98 @@ pcp_node_info(PCPConnInfo * pcpConn, int nid)
        return process_pcp_response(pcpConn, 'I');
 }
 
+
+/* --------------------------------
+ * pcp_health_check_stats - get information of health check stats pointed by given argument
+ *
+ * return structure of node information on success, -1 otherwise
+ * --------------------------------
+ */
+PCPResultInfo *
+pcp_health_check_stats(PCPConnInfo * pcpConn, int nid)
+{
+       int                     wsize;
+       char            node_id[16];
+
+       if (PCPConnectionStatus(pcpConn) != PCP_CONNECTION_OK)
+       {
+               pcp_internal_error(pcpConn,
+                                                  "invalid PCP connection");
+               return NULL;
+       }
+
+       snprintf(node_id, sizeof(node_id), "%d", nid);
+
+       pcp_write(pcpConn->pcpConn, "H", 1);
+       wsize = htonl(strlen(node_id) + 1 + sizeof(int));
+       pcp_write(pcpConn->pcpConn, &wsize, sizeof(int));
+       pcp_write(pcpConn->pcpConn, node_id, strlen(node_id) + 1);
+       if (PCPFlush(pcpConn) < 0)
+               return NULL;
+       if (pcpConn->Pfdebug)
+               fprintf(pcpConn->Pfdebug, "DEBUG: send: tos=\"L\", len=%d\n", ntohl(wsize));
+
+       return process_pcp_response(pcpConn, 'H');
+}
+
+/*
+ * Process health check response from PCP server.
+ * pcpConn: connection to the server
+ * buf:                returned data from server
+ * len:                length of the data
+ */
+static void
+process_health_check_stats_response
+(PCPConnInfo * pcpConn, char *buf, int len)
+{
+       POOL_HEALTH_CHECK_STATS *stats;
+       int             *offsets;
+       int             n;
+       int             i;
+       char    *p;
+       int             maxstr;
+       char    c[] = "CommandComplete";
+
+       if (strcmp(buf, c) != 0)
+       {
+               pcp_internal_error(pcpConn,
+                                                  "command failed. invalid response");
+               setResultStatus(pcpConn, PCP_RES_BAD_RESPONSE);
+               return;
+       }
+       buf += sizeof(c);
+
+       /* Allocate health stats memory */
+       stats = palloc0(sizeof(POOL_HEALTH_CHECK_STATS));
+       p = (char *)stats;
+
+       /* Calculate total packet length */
+       offsets = pool_health_check_stats_offsets(&n);
+
+       for (i = 0; i < n; i++)
+       {
+               if (i == n -1)
+                       maxstr = sizeof(POOL_HEALTH_CHECK_STATS) - offsets[i];
+               else
+                       maxstr = offsets[i + 1] - offsets[i];
+
+               StrNCpy(p + offsets[i], buf, maxstr -1);
+               buf += strlen(buf) + 1;
+       }
+
+       if (setNextResultBinaryData(pcpConn->pcpResInfo, (void *) stats, sizeof(POOL_HEALTH_CHECK_STATS), NULL) < 0)
+       {
+               if (stats)
+                       pfree(stats);
+               pcp_internal_error(pcpConn,
+                                                  "command failed. invalid response");
+               setResultStatus(pcpConn, PCP_RES_BAD_RESPONSE);
+       }
+       else
+               setCommandSuccessful(pcpConn);
+
+}
+
 static void
 process_process_count_response(PCPConnInfo * pcpConn, char *buf, int len)
 {
diff --git a/src/pcp_con/README.add_pcp_command b/src/pcp_con/README.add_pcp_command
new file mode 100644 (file)
index 0000000..43da89d
--- /dev/null
@@ -0,0 +1,51 @@
+How to add new PCP protocol
+
+src/pcp_con/pcp_worker.c is the server side process. We use "health
+check stats" as an example.
+
+1. In pcp_process_command() add case statement dedicated for each
+protocol. Typically server side protocol is indicated by single ASCII
+character (in the example 'H'). In the case statement,
+set_ps_display() is called to show what the PCP server is doing. Then
+"inform_xxx" function (in the example inform_health_check_stats) is
+called. this function is responsible for creating packet and sends to
+PCP frontend. The parameter is "pcp_frontend" which is the conection
+object to the PCP frontend and "buf" which includes data send from the
+PCP client.
+
+2. inform_xxx
+
+In inform_xxx (in the example inform_health_check_stats), first parse
+the data sent from client. In this particular example it's the backend
+id which should be used to create packet data.
+
+In the function first send character indicating the protocol using
+pcp_write(). Usually the character is lower case of the server side
+protocol character (in the example 'h').
+
+Next send an 4 byte integer representing the packet length in network
+byte order. Note that the length includes the interger itself.
+
+Finally send the protocol body followed by do_pcp_flush() which flush
+out the internal network write buffer.
+
+3. client side
+
+The main source code is src/tools/pcp/pcp_frontend_client.c.
+
+- output_xxx function needs to be defined.
+
+- add PCP_HEALTH_CHECK_STATS to enum PCP_UTILITIES.
+
+- add command name "pcp_health_check_stats" to struct AppTypes AllAppTypes[].
+
+- add  "else if" below
+       /*
+        * Okay the connection is successful not call the actual PCP function
+        */
+       if (current_app_type->app_type == PCP_ATTACH_NODE)
+
+- add call to the output_xxx function.
+
+               if (current_app_type->app_type == PCP_HEALTH_CHECK_STATS)
+                       output_health_check_stats_result(pcpResInfo, verbose);
index 72c4a9b6173788d4bd0e64e451abff9ed33beec3..cd66bc223baef4d708ce3555c93c119b54e60472 100644 (file)
@@ -4,7 +4,7 @@
  * pgpool: a language independent connection pool server for PostgreSQL
  * written by Tatsuo Ishii
  *
- * Copyright (c) 2003-2019     PgPool Global Development Group
+ * Copyright (c) 2003-2020     PgPool Global Development Group
  *
  * Permission to use, copy, modify, and distribute this software and
  * its documentation for any purpose and without fee is hereby
@@ -74,6 +74,7 @@ static void inform_process_info(PCP_CONNECTION * frontend, char *buf);
 static void inform_watchdog_info(PCP_CONNECTION * frontend, char *buf);
 static void inform_node_info(PCP_CONNECTION * frontend, char *buf);
 static void inform_node_count(PCP_CONNECTION * frontend);
+static void inform_health_check_stats(PCP_CONNECTION *frontend, char *buf);
 static void process_detach_node(PCP_CONNECTION * frontend, char *buf, char tos);
 static void process_attach_node(PCP_CONNECTION * frontend, char *buf);
 static void process_recovery_request(PCP_CONNECTION * frontend, char *buf);
@@ -217,7 +218,6 @@ pcp_worker_main(int port)
 static void
 pcp_process_command(char tos, char *buf, int buf_len)
 {
-
        if (tos == 'O' || tos == 'T')
        {
                if (Req_info->switching)
@@ -260,6 +260,11 @@ pcp_process_command(char tos, char *buf, int buf_len)
                        inform_node_count(pcp_frontend);
                        break;
 
+               case 'H':                               /* health check stats */
+                       set_ps_display("PCP: processing health check stats request", false);
+                       inform_health_check_stats(pcp_frontend, buf);
+                       break;
+
                case 'I':                               /* node info */
                        set_ps_display("PCP: processing node info request", false);
                        inform_node_info(pcp_frontend, buf);
@@ -891,6 +896,84 @@ inform_node_info(PCP_CONNECTION * frontend, char *buf)
        do_pcp_flush(frontend);
 }
 
+/*
+ * Send out health check stats data to pcp client.  node id is provided as a
+ * string in buf parameter.
+ *
+ * The protocol starts with 'h', followed by 4-byte packet length integer in
+ * network byte order including self.  Each data is represented as a null
+ * terminted string. The order of each data is defined in
+ * POOL_HEALTH_CHECK_STATS struct.
+ */
+static void
+inform_health_check_stats(PCP_CONNECTION *frontend, char *buf)
+{
+       POOL_HEALTH_CHECK_STATS *stats;
+       POOL_HEALTH_CHECK_STATS *s;
+       int             *offsets;
+       int             n;
+       int             nrows;
+       int             i;
+       int             node_id;
+       bool    node_id_ok = false;
+       int             wsize;
+       char    code[] = "CommandComplete";
+
+       node_id = atoi(buf);
+
+       if (node_id < 0 || node_id > NUM_BACKENDS)
+       {
+               ereport(ERROR,
+                               (errmsg("informing health check stats info failed"),
+                                errdetail("invalid node ID %d", node_id)));
+       }
+       
+       stats = get_health_check_stats(&nrows);
+
+       for (i = 0; i < nrows; i++)
+       {
+               if (atoi(stats[i].node_id) == node_id)
+               {
+                       node_id_ok = true;
+                       s = &stats[i];
+                       break;
+               }
+       }
+
+       if (!node_id_ok)
+       {
+               ereport(ERROR,
+                               (errmsg("informing health check stats info failed"),
+                                errdetail("stats data for node ID %d does not exist", node_id)));
+       }
+
+       pcp_write(frontend, "h", 1);    /* indicate that this is a reply to health check stats request */
+
+       wsize = sizeof(code) + sizeof(int);
+
+       /* Calculate total packet length */
+       offsets = pool_health_check_stats_offsets(&n);
+
+       for (i = 0; i < n; i++)
+       {
+               wsize += strlen((char *)s + offsets[i]) + 1;
+       }
+       wsize = htonl(wsize);   /* convert to network byte order */
+
+       /* send packet length to frontend */
+       pcp_write(frontend, &wsize, sizeof(int));
+       /* send "Command Complete" to frontend */
+       pcp_write(frontend, code, sizeof(code));
+
+       /* send each health check stats data to frontend */
+       for (i = 0; i < n; i++)
+       {
+               pcp_write(frontend, (char *)s + offsets[i], strlen((char *)s + offsets[i]) + 1);
+       }
+       pfree(stats);
+       do_pcp_flush(frontend);
+}
+
 static void
 inform_node_count(PCP_CONNECTION * frontend)
 {
index 19fbaeb2f3966250801438eeb90fbbf333c5de44..4c4a063df573c93e59ff005ed4b6de08c8f9bb3e 100644 (file)
@@ -4,6 +4,7 @@ AM_CPPFLAGS = -D_GNU_SOURCE -DPOOL_PRIVATE -I @PGSQL_INCLUDE_DIR@
 bin_PROGRAMS =  pcp_stop_pgpool \
                                pcp_node_count \
                                pcp_node_info \
+                               pcp_health_check_stats \
                                pcp_proc_count \
                                pcp_proc_info \
                                pcp_detach_node \
@@ -20,6 +21,8 @@ pcp_stop_pgpool_LDADD = $(libs_dir)/pcp/libpcp.la
 pcp_node_count_SOURCES = $(client_sources)
 pcp_node_count_LDADD = $(libs_dir)/pcp/libpcp.la
 pcp_node_info_SOURCES = $(client_sources)
+pcp_health_check_stats_LDADD = $(libs_dir)/pcp/libpcp.la
+pcp_health_check_stats_SOURCES = $(client_sources) ../../utils/pool_health_check_stats.c
 pcp_node_info_LDADD = $(libs_dir)/pcp/libpcp.la
 pcp_proc_count_SOURCES = $(client_sources)
 pcp_proc_count_LDADD = $(libs_dir)/pcp/libpcp.la
index fa815572552531dca7f5a0c36cb01668fb165889..b89c5030f408c66de046013dc768102f04eb2b19 100644 (file)
@@ -4,7 +4,7 @@
  * pgpool: a language independent connection pool server for PostgreSQL
  * written by Tatsuo Ishii
  *
- * Copyright (c) 2003-2019     PgPool Global Development Group
+ * Copyright (c) 2003-2020     PgPool Global Development Group
  *
  * Permission to use, copy, modify, and distribute this software and
  * its documentation for any purpose and without fee is hereby
@@ -48,6 +48,7 @@ static void output_procinfo_result(PCPResultInfo * pcpResInfo, bool all, bool ve
 static void output_proccount_result(PCPResultInfo * pcpResInfo, bool verbose);
 static void output_poolstatus_result(PCPResultInfo * pcpResInfo, bool verbose);
 static void output_nodeinfo_result(PCPResultInfo * pcpResInfo, bool verbose);
+static void output_health_check_stats_result(PCPResultInfo * pcpResInfo, bool verbose);
 static void output_nodecount_result(PCPResultInfo * pcpResInfo, bool verbose);
 static char *backend_status_to_string(BackendInfo * bi);
 static char *format_titles(const char **titles, const char **types, int ntitles);
@@ -58,6 +59,7 @@ typedef enum
        PCP_DETACH_NODE,
        PCP_NODE_COUNT,
        PCP_NODE_INFO,
+       PCP_HEALTH_CHECK_STATS,
        PCP_POOL_STATUS,
        PCP_PROC_COUNT,
        PCP_PROC_INFO,
@@ -82,6 +84,7 @@ struct AppTypes AllAppTypes[] =
        {"pcp_detach_node", PCP_DETACH_NODE, "n:h:p:U:gwWvd", "detach a node from pgpool-II"},
        {"pcp_node_count", PCP_NODE_COUNT, "h:p:U:wWvd", "display the total number of nodes under pgpool-II's control"},
        {"pcp_node_info", PCP_NODE_INFO, "n:h:p:U:wWvd", "display a pgpool-II node's information"},
+       {"pcp_health_check_stats", PCP_HEALTH_CHECK_STATS, "n:h:p:U:wWvd", "display a pgpool-II health check stats data"},
        {"pcp_pool_status", PCP_POOL_STATUS, "h:p:U:wWvd", "display pgpool configuration and status"},
        {"pcp_proc_count", PCP_PROC_COUNT, "h:p:U:wWvd", "display the list of pgpool-II child process PIDs"},
        {"pcp_proc_info", PCP_PROC_INFO, "h:p:P:U:awWvd", "display a pgpool-II child process' information"},
@@ -366,6 +369,11 @@ main(int argc, char **argv)
                pcpResInfo = pcp_node_info(pcpConn, nodeID);
        }
 
+       else if (current_app_type->app_type == PCP_HEALTH_CHECK_STATS)
+       {
+               pcpResInfo = pcp_health_check_stats(pcpConn, nodeID);
+       }
+
        else if (current_app_type->app_type == PCP_POOL_STATUS)
        {
                pcpResInfo = pcp_pool_status(pcpConn);
@@ -429,6 +437,9 @@ main(int argc, char **argv)
                if (current_app_type->app_type == PCP_NODE_INFO)
                        output_nodeinfo_result(pcpResInfo, verbose);
 
+               if (current_app_type->app_type == PCP_HEALTH_CHECK_STATS)
+                       output_health_check_stats_result(pcpResInfo, verbose);
+
                if (current_app_type->app_type == PCP_POOL_STATUS)
                        output_poolstatus_result(pcpResInfo, verbose);
 
@@ -508,6 +519,75 @@ output_nodeinfo_result(PCPResultInfo * pcpResInfo, bool verbose)
        }
 }
 
+/*
+ * Format and output health check stats
+ */
+static void
+output_health_check_stats_result(PCPResultInfo * pcpResInfo, bool verbose)
+{
+       POOL_HEALTH_CHECK_STATS *stats = (POOL_HEALTH_CHECK_STATS *)pcp_get_binary_data(pcpResInfo, 0);
+
+       if (verbose)
+       {
+               const char *titles[] = {"Node Id", "Host Name", "Port", "Status", "Role", "Last Status Change",
+                                                               "Total Count", "Success Count", "Fail Count", "Skip Count", "Retry Count",
+                                                               "Average Retry Count", "Max Retry Count", "Max Health Check Duration",
+                                                               "Minimum Health Check Duration", "Average Health Check Duration",
+                                                               "Last Health Check", "Last Successful Health Check",
+                                                               "Last Skip Health Check", "Last Failed Health Check"};
+               const char *types[] = {"s", "s", "s", "s", "s", "s", "s", "s", "s", "s",
+                                                          "s", "s", "s", "s", "s", "s", "s", "s", "s", "s"};
+               char *format_string;
+
+               format_string = format_titles(titles, types, sizeof(titles)/sizeof(char *));
+               printf(format_string,
+                          stats->node_id,
+                          stats->hostname,
+                          stats->port,
+                          stats->status,
+                          stats->role,
+                          stats->last_status_change,
+                          stats->total_count,
+                          stats->success_count,
+                          stats->fail_count,
+                          stats->skip_count,
+                          stats->retry_count,
+                          stats->average_retry_count,
+                          stats->max_retry_count,
+                          stats->max_health_check_duration,
+                          stats->min_health_check_duration,
+                          stats->average_health_check_duration,
+                          stats->last_health_check,
+                          stats->last_successful_health_check,
+                          stats->last_skip_health_check,
+                          stats->last_failed_health_check);
+       }
+       else
+       {
+               printf("%s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s\n",
+                          stats->node_id,
+                          stats->hostname,
+                          stats->port,
+                          stats->status,
+                          stats->role,
+                          stats->last_status_change,
+                          stats->total_count,
+                          stats->success_count,
+                          stats->fail_count,
+                          stats->skip_count,
+                          stats->retry_count,
+                          stats->average_retry_count,
+                          stats->max_retry_count,
+                          stats->max_health_check_duration,
+                          stats->min_health_check_duration,
+                          stats->average_health_check_duration,
+                          stats->last_health_check,
+                          stats->last_successful_health_check,
+                          stats->last_skip_health_check,
+                          stats->last_failed_health_check);
+       }
+}
+
 static void
 output_poolstatus_result(PCPResultInfo * pcpResInfo, bool verbose)
 {
@@ -701,6 +781,7 @@ app_require_nodeID(void)
        return (current_app_type->app_type == PCP_ATTACH_NODE ||
                        current_app_type->app_type == PCP_DETACH_NODE ||
                        current_app_type->app_type == PCP_NODE_INFO ||
+                       current_app_type->app_type == PCP_HEALTH_CHECK_STATS ||
                        current_app_type->app_type == PCP_PROMOTE_NODE ||
                        current_app_type->app_type == PCP_RECOVERY_NODE);
 }
diff --git a/src/utils/pool_health_check_stats.c b/src/utils/pool_health_check_stats.c
new file mode 100644 (file)
index 0000000..d3d653b
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * $Header$
+ *
+ * pgpool: a language independent connection pool server for PostgreSQL
+ * written by Tatsuo Ishii
+ *
+ * Copyright (c) 2003-2020     PgPool Global Development Group
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that copyright notice and this permission
+ * notice appear in supporting documentation, and that the name of the
+ * author not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. The author makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ */
+
+#include <stddef.h>
+#include "pool.h"
+#include "pcp/libpcp_ext.h"
+
+/*
+ * Returns an array consisting of POOL_HEALTH_CHECK_STATS struct member
+ * offsets.  The reason why we have this as a function is the table data needs
+ * to be shared by both PCP server and clients.
+ * Number of struct members will be stored in *n.
+ */
+int * pool_health_check_stats_offsets(int *n)
+{
+       static  int     offsettbl[] = {
+               offsetof(POOL_HEALTH_CHECK_STATS, node_id),
+               offsetof(POOL_HEALTH_CHECK_STATS, hostname),
+               offsetof(POOL_HEALTH_CHECK_STATS, port),
+               offsetof(POOL_HEALTH_CHECK_STATS, status),
+               offsetof(POOL_HEALTH_CHECK_STATS, role),
+               offsetof(POOL_HEALTH_CHECK_STATS, last_status_change),
+               offsetof(POOL_HEALTH_CHECK_STATS, total_count),
+               offsetof(POOL_HEALTH_CHECK_STATS, success_count),
+               offsetof(POOL_HEALTH_CHECK_STATS, fail_count),
+               offsetof(POOL_HEALTH_CHECK_STATS, skip_count),
+               offsetof(POOL_HEALTH_CHECK_STATS, retry_count),
+               offsetof(POOL_HEALTH_CHECK_STATS, average_retry_count),
+               offsetof(POOL_HEALTH_CHECK_STATS, max_retry_count),
+               offsetof(POOL_HEALTH_CHECK_STATS, max_health_check_duration),
+               offsetof(POOL_HEALTH_CHECK_STATS, min_health_check_duration),
+               offsetof(POOL_HEALTH_CHECK_STATS, average_health_check_duration),
+               offsetof(POOL_HEALTH_CHECK_STATS, last_health_check),
+               offsetof(POOL_HEALTH_CHECK_STATS, last_successful_health_check),
+               offsetof(POOL_HEALTH_CHECK_STATS, last_skip_health_check),
+               offsetof(POOL_HEALTH_CHECK_STATS, last_failed_health_check),
+       };
+
+       *n = sizeof(offsettbl)/sizeof(int);
+       return offsettbl;
+}
index f15367685073a124c6d3af500dbf68f88854e36a..25728b0ed66e0b756c08e5a60a8c61f65ed3e14d 100644 (file)
@@ -2017,7 +2017,6 @@ get_health_check_stats(int *nrows)
        int                     i;
        POOL_HEALTH_CHECK_STATS *stats = palloc0(NUM_BACKENDS * sizeof(POOL_HEALTH_CHECK_STATS));
        BackendInfo *bi = NULL;
-       struct tm       tm;
        time_t          t;
        double          f;
 
@@ -2050,8 +2049,9 @@ get_health_check_stats(int *nrows)
                }
 
                /* status last changed */
-               localtime_r(&bi->status_changed_time, &tm);
-               strftime(stats[i].last_status_change, POOLCONFIG_MAXDATELEN, "%F %T", &tm);
+               t = bi->status_changed_time;
+               ereport(LOG,(errmsg("status_changed_time %ld", t)));
+               strftime(stats[i].last_status_change, POOLCONFIG_MAXDATELEN, "%F %T", localtime(&t));
 
                snprintf(stats[i].total_count, POOLCONFIG_MAXLONGCOUNTLEN, UINT64_FORMAT, health_check_stats[i].total_count);
                snprintf(stats[i].success_count, POOLCONFIG_MAXLONGCOUNTLEN, UINT64_FORMAT, health_check_stats[i].success_count);