pgpool2.git / summary / log / commit / refs
commit 42919f2e187f7fcf0d09fe532fa00f2d672a81e0
Author: Tatsuo Ishii <ishii@sraoss.co.jp>
Date: Mon Feb 24 13:29:24 2020 +0000
Add pcp_healt_check_stats command.
doc.ja/src/sgml/ref/allfiles.sgml | 1 +
doc.ja/src/sgml/ref/pcp_health_check_stats.sgml | 103 ++++++++++++++++++++++++
doc.ja/src/sgml/reference.sgml | 1 +
doc/src/sgml/ref/allfiles.sgml | 1 +
doc/src/sgml/ref/pcp_health_check_stats.sgml | 102 +++++++++++++++++++++++
doc/src/sgml/reference.sgml | 1 +
src/Makefile.am | 3 +-
src/include/pcp/libpcp_ext.h | 4 +-
src/libs/pcp/Makefile.am | 3 +-
src/libs/pcp/pcp.c | 103 +++++++++++++++++++++++-
src/pcp_con/README.add_pcp_command | 51 ++++++++++++
src/pcp_con/pcp_worker.c | 87 +++++++++++++++++++-
src/tools/pcp/Makefile.am | 3 +
src/tools/pcp/pcp_frontend_client.c | 83 ++++++++++++++++++-
src/utils/pool_health_check_stats.c | 59 ++++++++++++++
src/utils/pool_process_reporting.c | 6 +-
16 files changed, 601 insertions(+), 10 deletions(-)
diff --git a/doc.ja/src/sgml/ref/allfiles.sgml b/doc.ja/src/sgml/ref/allfiles.sgml
index 7a643ccaf..195ae28db 100644
--- a/doc.ja/src/sgml/ref/allfiles.sgml
+++ b/doc.ja/src/sgml/ref/allfiles.sgml
@@ -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
index 000000000..2a9deea29
--- /dev/null
+++ b/doc.ja/src/sgml/ref/pcp_health_check_stats.sgml
@@ -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>
diff --git a/doc.ja/src/sgml/reference.sgml b/doc.ja/src/sgml/reference.sgml
index 75982801b..2393fef0f 100644
--- a/doc.ja/src/sgml/reference.sgml
+++ b/doc.ja/src/sgml/reference.sgml
@@ -145,6 +145,7 @@
&pcpCommonOptions;
&pcpNodeCount;
&pcpNodeInfo;
+ &pcpHealthCheckStats;
&pcpWatchdogInfo;
&pcpProcCount;
&pcpProcInfo;
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index 7a643ccaf..195ae28db 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -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
index 000000000..824b6341d
--- /dev/null
+++ b/doc/src/sgml/ref/pcp_health_check_stats.sgml
@@ -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>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index 17cb8843a..5ba2892fb 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -95,6 +95,7 @@
&pcpCommonOptions;
&pcpNodeCount;
&pcpNodeInfo;
+ &pcpHealthCheckStats;
&pcpWatchdogInfo;
&pcpProcCount;
&pcpProcInfo;
diff --git a/src/Makefile.am b/src/Makefile.am
index 34e95b4a1..8edb820a2 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -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)\" \
diff --git a/src/include/pcp/libpcp_ext.h b/src/include/pcp/libpcp_ext.h
index 8b3a91ab1..caf05bbdc 100644
--- a/src/include/pcp/libpcp_ext.h
+++ b/src/include/pcp/libpcp_ext.h
@@ -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
* ------------------------------
diff --git a/src/libs/pcp/Makefile.am b/src/libs/pcp/Makefile.am
index ccd9115bb..feecdf49c 100644
--- a/src/libs/pcp/Makefile.am
+++ b/src/libs/pcp/Makefile.am
@@ -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
diff --git a/src/libs/pcp/pcp.c b/src/libs/pcp/pcp.c
index 8a982ad40..788d7224a 100644
--- a/src/libs/pcp/pcp.c
+++ b/src/libs/pcp/pcp.c
@@ -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
index 000000000..43da89d33
--- /dev/null
+++ b/src/pcp_con/README.add_pcp_command
@@ -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);
diff --git a/src/pcp_con/pcp_worker.c b/src/pcp_con/pcp_worker.c
index 72c4a9b61..cd66bc223 100644
--- a/src/pcp_con/pcp_worker.c
+++ b/src/pcp_con/pcp_worker.c
@@ -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)
{
diff --git a/src/tools/pcp/Makefile.am b/src/tools/pcp/Makefile.am
index 19fbaeb2f..4c4a063df 100644
--- a/src/tools/pcp/Makefile.am
+++ b/src/tools/pcp/Makefile.am
@@ -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
diff --git a/src/tools/pcp/pcp_frontend_client.c b/src/tools/pcp/pcp_frontend_client.c
index fa8155725..b89c5030f 100644
--- a/src/tools/pcp/pcp_frontend_client.c
+++ b/src/tools/pcp/pcp_frontend_client.c
@@ -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
index 000000000..d3d653b47
--- /dev/null
+++ b/src/utils/pool_health_check_stats.c
@@ -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;
+}
diff --git a/src/utils/pool_process_reporting.c b/src/utils/pool_process_reporting.c
index f15367685..25728b0ed 100644
--- a/src/utils/pool_process_reporting.c
+++ b/src/utils/pool_process_reporting.c
@@ -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);
[parent: 748ef78115c0]