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]