injection_points: Add some fixed-numbered statistics
authorMichael Paquier <michael@paquier.xyz>
Mon, 5 Aug 2024 03:29:22 +0000 (12:29 +0900)
committerMichael Paquier <michael@paquier.xyz>
Mon, 5 Aug 2024 03:29:22 +0000 (12:29 +0900)
Like 75534436a477, this acts mainly as a template to show what can be
achieved with fixed-numbered stats (like WAL, bgwriter, etc.) with the
pluggable cumulative statistics APIs introduced in 7949d9594582.

Fixed-numbered stats are defined in their own file, named
injection_stats_fixed.c, separated entirely from the variable-numbered
case in injection_stats.c.  This is mainly for clarity as having both
examples in the same file would be confusing.

Note that this commit uses the helper routines added in 2eff9e678d35.
The stats stored track globally the number of times injection points
have been attached, detached or run.  Two more fields should be added
later for the number of times a point has been cached or loaded, but
what's here is enough as a template.

More TAP tests are added, providing coverage for fixed-numbered custom
stats.

Author: Michael Paquier
Reviewed-by: Dmitry Dolgov, Bertrand Drouvot
Discussion: https://postgr.es/m/Zmqm9j5EO0I4W8dx@paquier.xyz

src/test/modules/injection_points/Makefile
src/test/modules/injection_points/injection_points--1.0.sql
src/test/modules/injection_points/injection_points.c
src/test/modules/injection_points/injection_stats.h
src/test/modules/injection_points/injection_stats_fixed.c [new file with mode: 0644]
src/test/modules/injection_points/meson.build
src/test/modules/injection_points/t/001_stats.pl
src/tools/pgindent/typedefs.list

index 7b9cd12a2a9d82fe52dcf17513bb8419f47c0dce..ed28cd13a8b49294fd7d54b3ff57beb4e51ac847 100644 (file)
@@ -4,7 +4,8 @@ MODULE_big = injection_points
 OBJS = \
    $(WIN32RES) \
    injection_points.o \
-   injection_stats.o
+   injection_stats.o \
+   injection_stats_fixed.o
 EXTENSION = injection_points
 DATA = injection_points--1.0.sql
 PGFILEDESC = "injection_points - facility for injection points"
index b98d571889037978c217bbc68556dab5ada7348a..1b2a4938a95fbb5f3a03f0284ebbaf9684ca7811 100644 (file)
@@ -84,3 +84,14 @@ CREATE FUNCTION injection_points_stats_numcalls(IN point_name TEXT)
 RETURNS bigint
 AS 'MODULE_PATHNAME', 'injection_points_stats_numcalls'
 LANGUAGE C STRICT;
+
+--
+-- injection_points_stats_fixed()
+--
+-- Reports fixed-numbered statistics for injection points.
+CREATE FUNCTION injection_points_stats_fixed(OUT numattach int8,
+   OUT numdetach int8,
+   OUT numrun int8)
+RETURNS record
+AS 'MODULE_PATHNAME', 'injection_points_stats_fixed'
+LANGUAGE C STRICT;
index acec4e95b0501474698fce43ff4ae12d88f2fe97..dc02be1bbf6367aa834cd9fb35f8864a05590afa 100644 (file)
@@ -297,6 +297,7 @@ injection_points_attach(PG_FUNCTION_ARGS)
        condition.pid = MyProcPid;
    }
 
+   pgstat_report_inj_fixed(1, 0, 0);
    InjectionPointAttach(name, "injection_points", function, &condition,
                         sizeof(InjectionPointCondition));
 
@@ -342,6 +343,7 @@ injection_points_run(PG_FUNCTION_ARGS)
 {
    char       *name = text_to_cstring(PG_GETARG_TEXT_PP(0));
 
+   pgstat_report_inj_fixed(0, 0, 1);
    INJECTION_POINT(name);
 
    PG_RETURN_VOID();
@@ -432,6 +434,7 @@ injection_points_detach(PG_FUNCTION_ARGS)
 {
    char       *name = text_to_cstring(PG_GETARG_TEXT_PP(0));
 
+   pgstat_report_inj_fixed(0, 1, 0);
    if (!InjectionPointDetach(name))
        elog(ERROR, "could not detach injection point \"%s\"", name);
 
@@ -459,4 +462,5 @@ _PG_init(void)
        return;
 
    pgstat_register_inj();
+   pgstat_register_inj_fixed();
 }
index 3e9970548345d23a1bd10b0a001d9d3a0dca8d4d..d519f29f832e07f8c5b01f0e77be0727a6d1035d 100644 (file)
 #ifndef INJECTION_STATS
 #define INJECTION_STATS
 
+/* injection_stats.c */
 extern void pgstat_register_inj(void);
 extern void pgstat_create_inj(const char *name);
 extern void pgstat_drop_inj(const char *name);
 extern void pgstat_report_inj(const char *name);
 
+/* injection_stats_fixed.c */
+extern void pgstat_register_inj_fixed(void);
+extern void pgstat_report_inj_fixed(uint32 numattach,
+                                   uint32 numdetach,
+                                   uint32 numrun);
+
 #endif
diff --git a/src/test/modules/injection_points/injection_stats_fixed.c b/src/test/modules/injection_points/injection_stats_fixed.c
new file mode 100644 (file)
index 0000000..72a5f9d
--- /dev/null
@@ -0,0 +1,192 @@
+/*--------------------------------------------------------------------------
+ *
+ * injection_stats_fixed.c
+ *     Code for fixed-numbered statistics of injection points.
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *     src/test/modules/injection_points/injection_stats_fixed.c
+ *
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "fmgr.h"
+
+#include "common/hashfn.h"
+#include "funcapi.h"
+#include "injection_stats.h"
+#include "pgstat.h"
+#include "utils/builtins.h"
+#include "utils/pgstat_internal.h"
+
+/* Structures for statistics of injection points, fixed-size */
+typedef struct PgStat_StatInjFixedEntry
+{
+   PgStat_Counter numattach;   /* number of points attached */
+   PgStat_Counter numdetach;   /* number of points detached */
+   PgStat_Counter numrun;      /* number of points run */
+   TimestampTz stat_reset_timestamp;
+} PgStat_StatInjFixedEntry;
+
+typedef struct PgStatShared_InjectionPointFixed
+{
+   LWLock      lock;           /* protects all the counters */
+   uint32      changecount;
+   PgStat_StatInjFixedEntry stats;
+   PgStat_StatInjFixedEntry reset_offset;
+} PgStatShared_InjectionPointFixed;
+
+/* Callbacks for fixed-numbered stats */
+static void injection_stats_fixed_init_shmem_cb(void *stats);
+static void injection_stats_fixed_reset_all_cb(TimestampTz ts);
+static void injection_stats_fixed_snapshot_cb(void);
+
+static const PgStat_KindInfo injection_stats_fixed = {
+   .name = "injection_points_fixed",
+   .fixed_amount = true,
+
+   .shared_size = sizeof(PgStat_StatInjFixedEntry),
+   .shared_data_off = offsetof(PgStatShared_InjectionPointFixed, stats),
+   .shared_data_len = sizeof(((PgStatShared_InjectionPointFixed *) 0)->stats),
+
+   .init_shmem_cb = injection_stats_fixed_init_shmem_cb,
+   .reset_all_cb = injection_stats_fixed_reset_all_cb,
+   .snapshot_cb = injection_stats_fixed_snapshot_cb,
+};
+
+/*
+ * Kind ID reserved for statistics of injection points.
+ */
+#define PGSTAT_KIND_INJECTION_FIXED    130
+
+/* Track if fixed-numbered stats are loaded */
+static bool inj_fixed_loaded = false;
+
+static void
+injection_stats_fixed_init_shmem_cb(void *stats)
+{
+   PgStatShared_InjectionPointFixed *stats_shmem =
+       (PgStatShared_InjectionPointFixed *) stats;
+
+   LWLockInitialize(&stats_shmem->lock, LWTRANCHE_PGSTATS_DATA);
+}
+
+static void
+injection_stats_fixed_reset_all_cb(TimestampTz ts)
+{
+   PgStatShared_InjectionPointFixed *stats_shmem =
+       pgstat_get_custom_shmem_data(PGSTAT_KIND_INJECTION_FIXED);
+
+   LWLockAcquire(&stats_shmem->lock, LW_EXCLUSIVE);
+   pgstat_copy_changecounted_stats(&stats_shmem->reset_offset,
+                                   &stats_shmem->stats,
+                                   sizeof(stats_shmem->stats),
+                                   &stats_shmem->changecount);
+   stats_shmem->stats.stat_reset_timestamp = ts;
+   LWLockRelease(&stats_shmem->lock);
+}
+
+static void
+injection_stats_fixed_snapshot_cb(void)
+{
+   PgStatShared_InjectionPointFixed *stats_shmem =
+       pgstat_get_custom_shmem_data(PGSTAT_KIND_INJECTION_FIXED);
+   PgStat_StatInjFixedEntry *stat_snap =
+       pgstat_get_custom_snapshot_data(PGSTAT_KIND_INJECTION_FIXED);
+   PgStat_StatInjFixedEntry *reset_offset = &stats_shmem->reset_offset;
+   PgStat_StatInjFixedEntry reset;
+
+   pgstat_copy_changecounted_stats(stat_snap,
+                                   &stats_shmem->stats,
+                                   sizeof(stats_shmem->stats),
+                                   &stats_shmem->changecount);
+
+   LWLockAcquire(&stats_shmem->lock, LW_SHARED);
+   memcpy(&reset, reset_offset, sizeof(stats_shmem->stats));
+   LWLockRelease(&stats_shmem->lock);
+
+   /* compensate by reset offsets */
+#define FIXED_COMP(fld) stat_snap->fld -= reset.fld;
+   FIXED_COMP(numattach);
+   FIXED_COMP(numdetach);
+   FIXED_COMP(numrun);
+#undef FIXED_COMP
+}
+
+/*
+ * Workhorse to do the registration work, called in _PG_init().
+ */
+void
+pgstat_register_inj_fixed(void)
+{
+   pgstat_register_kind(PGSTAT_KIND_INJECTION_FIXED, &injection_stats_fixed);
+
+   /* mark stats as loaded */
+   inj_fixed_loaded = true;
+}
+
+/*
+ * Report fixed number of statistics for an injection point.
+ */
+void
+pgstat_report_inj_fixed(uint32 numattach,
+                       uint32 numdetach,
+                       uint32 numrun)
+{
+   PgStatShared_InjectionPointFixed *stats_shmem;
+
+   /* leave if disabled */
+   if (!inj_fixed_loaded)
+       return;
+
+   stats_shmem = pgstat_get_custom_shmem_data(PGSTAT_KIND_INJECTION_FIXED);
+
+   pgstat_begin_changecount_write(&stats_shmem->changecount);
+   stats_shmem->stats.numattach += numattach;
+   stats_shmem->stats.numdetach += numdetach;
+   stats_shmem->stats.numrun += numrun;
+   pgstat_end_changecount_write(&stats_shmem->changecount);
+}
+
+/*
+ * SQL function returning fixed-numbered statistics for injection points.
+ */
+PG_FUNCTION_INFO_V1(injection_points_stats_fixed);
+Datum
+injection_points_stats_fixed(PG_FUNCTION_ARGS)
+{
+   TupleDesc   tupdesc;
+   Datum       values[3] = {0};
+   bool        nulls[3] = {0};
+   PgStat_StatInjFixedEntry *stats;
+
+   if (!inj_fixed_loaded)
+       PG_RETURN_NULL();
+
+   pgstat_snapshot_fixed(PGSTAT_KIND_INJECTION_FIXED);
+   stats = pgstat_get_custom_snapshot_data(PGSTAT_KIND_INJECTION_FIXED);
+
+   /* Initialise attributes information in the tuple descriptor */
+   tupdesc = CreateTemplateTupleDesc(3);
+   TupleDescInitEntry(tupdesc, (AttrNumber) 1, "numattach",
+                      INT8OID, -1, 0);
+   TupleDescInitEntry(tupdesc, (AttrNumber) 2, "numdetach",
+                      INT8OID, -1, 0);
+   TupleDescInitEntry(tupdesc, (AttrNumber) 3, "numrun",
+                      INT8OID, -1, 0);
+   BlessTupleDesc(tupdesc);
+
+   values[0] = Int64GetDatum(stats->numattach);
+   values[1] = Int64GetDatum(stats->numdetach);
+   values[2] = Int64GetDatum(stats->numrun);
+   nulls[0] = false;
+   nulls[1] = false;
+   nulls[2] = false;
+
+   /* Returns the record as Datum */
+   PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
+}
index a52fe5121e524ba4cb824a8d9f1c5b889c9ec4c3..c9e357f644116ddb98d321b3f1db3a83ad372bae 100644 (file)
@@ -7,6 +7,7 @@ endif
 injection_points_sources = files(
   'injection_points.c',
   'injection_stats.c',
+  'injection_stats_fixed.c',
 )
 
 if host_system == 'windows'
index fff805b46a99cebd53f32e6f81bdab86f59cce01..7897691f95487da35e7f389fe3206ff97128fa93 100644 (file)
@@ -33,18 +33,27 @@ $node->safe_psql('postgres', "SELECT injection_points_run('stats-notice');");
 my $numcalls = $node->safe_psql('postgres',
    "SELECT injection_points_stats_numcalls('stats-notice');");
 is($numcalls, '2', 'number of stats calls');
+my $fixedstats = $node->safe_psql('postgres',
+   "SELECT * FROM injection_points_stats_fixed();");
+is($fixedstats, '1|0|2', 'number of fixed stats');
 
 # Restart the node cleanly, stats should still be around.
 $node->restart;
 $numcalls = $node->safe_psql('postgres',
    "SELECT injection_points_stats_numcalls('stats-notice');");
 is($numcalls, '2', 'number of stats after clean restart');
+$fixedstats = $node->safe_psql('postgres',
+   "SELECT * FROM injection_points_stats_fixed();");
+is($fixedstats, '1|0|2', 'number of fixed stats after clean restart');
 
 # On crash the stats are gone.
 $node->stop('immediate');
 $node->start;
 $numcalls = $node->safe_psql('postgres',
    "SELECT injection_points_stats_numcalls('stats-notice');");
-is($numcalls, '', 'number of stats after clean restart');
+is($numcalls, '', 'number of stats after crash');
+$fixedstats = $node->safe_psql('postgres',
+   "SELECT * FROM injection_points_stats_fixed();");
+is($fixedstats, '0|0|0', 'number of fixed stats after crash');
 
 done_testing();
index 6cabdb02d6888f15a5e336d009ccc41272169f10..6e6b7c27118807038043171b16ef501fc2bed2a2 100644 (file)
@@ -2120,6 +2120,7 @@ PgStatShared_Database
 PgStatShared_Function
 PgStatShared_HashEntry
 PgStatShared_InjectionPoint
+PgStatShared_InjectionPointFixed
 PgStatShared_IO
 PgStatShared_Relation
 PgStatShared_ReplSlot
@@ -2152,6 +2153,7 @@ PgStat_SnapshotEntry
 PgStat_StatDBEntry
 PgStat_StatFuncEntry
 PgStat_StatInjEntry
+PgStat_StatInjFixedEntry
 PgStat_StatReplSlotEntry
 PgStat_StatSubEntry
 PgStat_StatTabEntry