1616 */
1717#include "postgres.h"
1818
19- #if PG_VERSION_NUM >= 180000
20-
2119#include "funcapi.h"
2220#include "utils/pgstat_internal.h"
2321
2422#include "spock.h"
23+
24+ PG_FUNCTION_INFO_V1 (spock_get_subscription_stats );
25+ PG_FUNCTION_INFO_V1 (spock_reset_subscription_stats );
26+
27+ #if PG_VERSION_NUM >= 180000
2528#include "spock_conflict_stat.h"
2629
2730/*
2831 * Kind ID reserved for statistics of spock replication conflicts.
29- * TODO: ask Michael Paquier about exact numbers and conflict detection
32+ * TODO: see https://wiki.postgresql.org/wiki/CustomCumulativeStats to choose
33+ * specific value in production
3034 */
31- #define SPOCK_PGSTAT_KIND_LRCONFLICTS 27
35+ #define SPOCK_PGSTAT_KIND_LRCONFLICTS 28
3236
3337/* Shared memory wrapper for spock subscription conflict stats */
3438typedef struct Spock_Stat_Subscription
3539{
3640 PgStatShared_Common header ;
37- Spock_Stat_StatSubEntry stats ;
41+ Spock_Stat_StatSubEntry stats ;
3842} Spock_Stat_Subscription ;
3943
4044/*
@@ -52,9 +56,6 @@ static const char *const SpockConflictStatColNames[SPOCK_CONFLICT_NUM_TYPES] = {
5256 [SPOCK_CT_DELETE_MISSING ] = "confl_delete_missing" ,
5357};
5458
55- PG_FUNCTION_INFO_V1 (spock_get_subscription_stats );
56- PG_FUNCTION_INFO_V1 (spock_reset_subscription_stats );
57-
5859static bool spock_stat_subscription_flush_cb (PgStat_EntryRef * entry_ref ,
5960 bool nowait );
6061static void spock_stat_subscription_reset_timestamp_cb (
@@ -95,7 +96,15 @@ spock_stat_report_subscription_conflict(Oid subid, SpockConflictType type)
9596 PgStat_EntryRef * entry_ref ;
9697 Spock_Stat_PendingSubEntry * pending ;
9798
98- Assert (type >= 0 && type < SPOCK_CONFLICT_NUM_TYPES );
99+ if (type != SPOCK_CT_UPDATE_MISSING )
100+ /*
101+ * Should happen only in development. Detect it as fast as possible
102+ * with the highest error level that does not crash the instance.
103+ */
104+ ereport (ERROR ,
105+ (errcode (ERRCODE_INTERNAL_ERROR ),
106+ errmsg ("unexpected conflict type %d reported for subscription %u" ,
107+ type , subid )));
99108
100109 entry_ref = pgstat_prep_pending_entry (SPOCK_PGSTAT_KIND_LRCONFLICTS ,
101110 MyDatabaseId , subid , NULL );
@@ -109,13 +118,31 @@ spock_stat_report_subscription_conflict(Oid subid, SpockConflictType type)
109118void
110119spock_stat_create_subscription (Oid subid )
111120{
121+ PgStat_EntryRef * ref ;
122+
112123 /* Ensures that stats are dropped if transaction rolls back */
113124 pgstat_create_transactional (SPOCK_PGSTAT_KIND_LRCONFLICTS ,
114125 MyDatabaseId , subid );
115126
116127 /* Create and initialize the subscription stats entry */
117- pgstat_get_entry_ref (SPOCK_PGSTAT_KIND_LRCONFLICTS , MyDatabaseId , subid ,
118- true, NULL );
128+ ref = pgstat_get_entry_ref (SPOCK_PGSTAT_KIND_LRCONFLICTS , MyDatabaseId , subid ,
129+ true, NULL );
130+
131+ if (pg_atomic_read_u32 (& ref -> shared_entry -> refcount ) != 2 )
132+ /*
133+ * Should never happen: a new subscription stats entry should have
134+ * exactly two references (the hashtable entry and our own). A higher
135+ * count means a stale entry from a previous subscription with the same
136+ * OID was not properly cleaned up.
137+ */
138+ ereport (WARNING ,
139+ (errmsg ("conflict statistics entry for subscription %u "
140+ "already has %u references" ,
141+ subid ,
142+ pg_atomic_read_u32 (& ref -> shared_entry -> refcount )),
143+ errhint ("This may indicate that a previous subscription with "
144+ "the same OID was not fully dropped." )));
145+
119146 pgstat_reset_entry (SPOCK_PGSTAT_KIND_LRCONFLICTS , MyDatabaseId , subid , 0 );
120147}
121148
@@ -247,9 +274,36 @@ spock_stat_subscription_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
247274}
248275
249276static void
250- spock_stat_subscription_reset_timestamp_cb (PgStatShared_Common * header , TimestampTz ts )
277+ spock_stat_subscription_reset_timestamp_cb (PgStatShared_Common * header ,
278+ TimestampTz ts )
251279{
252280 ((Spock_Stat_Subscription * ) header )-> stats .stat_reset_timestamp = ts ;
253281}
254282
255283#endif /* PG_VERSION_NUM >= 180000 */
284+
285+ #if PG_VERSION_NUM < 180000
286+
287+ /*
288+ * XXX: implement conflict statistics gathering, if needed
289+ */
290+
291+ Datum
292+ spock_get_subscription_stats (PG_FUNCTION_ARGS )
293+ {
294+ ereport (ERROR ,
295+ (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
296+ errmsg ("spock conflict statistics require PostgreSQL 18 or later" )));
297+ PG_RETURN_NULL (); /* unreachable; suppress compiler warning */
298+ }
299+
300+ Datum
301+ spock_reset_subscription_stats (PG_FUNCTION_ARGS )
302+ {
303+ ereport (ERROR ,
304+ (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
305+ errmsg ("spock conflict statistics require PostgreSQL 18 or later" )));
306+ PG_RETURN_NULL (); /* unreachable; suppress compiler warning */
307+ }
308+
309+ #endif /* PG_VERSION_NUM < 180000 */
0 commit comments