Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .github/workflows/installcheck.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ jobs:
version: latest

- name: Start docker cluster
id: start_cluster
run: |
cd ${GITHUB_WORKSPACE}/tests/docker/
# To minimize regression tests difference, override pgedge.env with
Expand All @@ -63,6 +64,22 @@ jobs:
PGVER=${{ matrix.pgver }} DBUSER=regression DBNAME=regression \
docker compose up --build --wait -d
timeout-minutes: 20
continue-on-error: true

- name: Diagnose cluster startup failure
if: steps.start_cluster.outcome == 'failure'
run: |
cd ${GITHUB_WORKSPACE}/tests/docker/
echo "=== Docker container status ==="
docker compose ps -a
for node in n1 n2 n3; do
echo ""
echo "=== Container logs: $node ==="
docker compose logs pgedge-$node 2>&1 | tail -80 || true
echo ""
echo "=== PostgreSQL logfile: $node ==="
docker compose cp pgedge-$node:/home/pgedge/logfile.log /dev/stdout 2>/dev/null | tail -80 || echo "(not available)"
done

- name: Run installcheck on node n1
id: installcheck
Expand Down
8 changes: 7 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,14 @@ all: spock.control
# -----------------------------------------------------------------------------
# Regression tests
# -----------------------------------------------------------------------------
# PG18+ only tests
REGRESS_PG18 =
ifeq ($(shell test $(PGVER) -ge 18 && echo yes),yes)
REGRESS_PG18 = conflict_stat
endif

REGRESS = preseed infofuncs init_fail init preseed_check basic conflict_secondary_unique \
excluded_schema \
excluded_schema $(REGRESS_PG18) \
toasted replication_set matview bidirectional primary_key \
interfaces foreign_key copy sequence triggers parallel functions row_filter \
row_filter_sampling att_list column_filter apply_delay \
Expand Down
8 changes: 7 additions & 1 deletion include/spock_conflict.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ extern bool spock_save_resolutions;
typedef enum
{
/* The row to be inserted violates unique constraint */
SPOCK_CT_INSERT_EXISTS,
SPOCK_CT_INSERT_EXISTS = 0,

/* The row to be updated was modified by a different origin */
SPOCK_CT_UPDATE_ORIGIN_DIFFERS,
Expand All @@ -76,6 +76,12 @@ typedef enum

} SpockConflictType;

/*
* SPOCK_CT_DELETE_LATE is excluded because it is not yet tracked in conflict
* statistics.
*/
#define SPOCK_CONFLICT_NUM_TYPES (SPOCK_CT_DELETE_MISSING + 1)

extern int spock_conflict_resolver;
extern int spock_conflict_log_level;
extern bool spock_save_resolutions;
Expand Down
46 changes: 46 additions & 0 deletions include/spock_conflict_stat.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*-------------------------------------------------------------------------
*
* spock_conflict_stat.h
* spock subscription conflict statistics
*
* Copyright (c) 2022-2026, pgEdge, Inc.
* Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, The Regents of the University of California
*
*-------------------------------------------------------------------------
*/
#ifndef SPOCK_CONFLICT_STAT_H
#define SPOCK_CONFLICT_STAT_H

#include "postgres.h"

#if PG_VERSION_NUM >= 180000

#include "pgstat.h"

#include "spock_conflict.h"

/* Shared memory stats entry for spock subscription conflicts */
typedef struct Spock_Stat_StatSubEntry
{
PgStat_Counter conflict_count[SPOCK_CONFLICT_NUM_TYPES];
TimestampTz stat_reset_timestamp;
} Spock_Stat_StatSubEntry;

/* Pending (backend-local) entry for spock subscription conflicts */
typedef struct Spock_Stat_PendingSubEntry
{
PgStat_Counter conflict_count[SPOCK_CONFLICT_NUM_TYPES];
} Spock_Stat_PendingSubEntry;

extern void spock_stat_register_conflict_stat(void);

extern void spock_stat_report_subscription_conflict(Oid subid,
SpockConflictType type);
extern void spock_stat_create_subscription(Oid subid);
extern void spock_stat_drop_subscription(Oid subid);
extern Spock_Stat_StatSubEntry *spock_stat_fetch_stat_subscription(Oid subid);

#endif /* PG_VERSION_NUM >= 180000 */

#endif /* SPOCK_CONFLICT_STAT_H */
23 changes: 23 additions & 0 deletions sql/spock--5.0.4--6.0.0-devel.sql
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,29 @@ SET conflict_type = CASE conflict_type
ELSE conflict_type
END;

-- ----
-- Subscription conflict statistics
-- ----
CREATE FUNCTION spock.get_subscription_stats(
subid oid,
OUT subid oid,
OUT confl_insert_exists bigint,
OUT confl_update_origin_differs bigint,
OUT confl_update_exists bigint,
OUT confl_update_missing bigint,
OUT confl_delete_origin_differs bigint,
OUT confl_delete_missing bigint,
OUT stats_reset timestamptz
)
RETURNS record
AS 'MODULE_PATHNAME', 'spock_get_subscription_stats'
LANGUAGE C STABLE;

CREATE FUNCTION spock.reset_subscription_stats(subid oid DEFAULT NULL)
RETURNS void
AS 'MODULE_PATHNAME', 'spock_reset_subscription_stats'
LANGUAGE C CALLED ON NULL INPUT VOLATILE;

-- Set delta_apply security label on specific column
CREATE FUNCTION spock.delta_apply(
rel regclass,
Expand Down
23 changes: 23 additions & 0 deletions sql/spock--6.0.0-devel.sql
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,29 @@ BEGIN
END;
$$ LANGUAGE plpgsql;

-- ----
-- Subscription conflict statistics
-- ----
CREATE FUNCTION spock.get_subscription_stats(
subid oid,
OUT subid oid,
OUT confl_insert_exists bigint,
OUT confl_update_origin_differs bigint,
OUT confl_update_exists bigint,
OUT confl_update_missing bigint,
OUT confl_delete_origin_differs bigint,
OUT confl_delete_missing bigint,
OUT stats_reset timestamptz
)
RETURNS record
AS 'MODULE_PATHNAME', 'spock_get_subscription_stats'
LANGUAGE C STABLE;

CREATE FUNCTION spock.reset_subscription_stats(subid oid DEFAULT NULL)
RETURNS void
AS 'MODULE_PATHNAME', 'spock_reset_subscription_stats'
LANGUAGE C CALLED ON NULL INPUT VOLATILE;

-- Set delta_apply security label on specific column
CREATE FUNCTION spock.delta_apply(
rel regclass,
Expand Down
8 changes: 8 additions & 0 deletions src/spock.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@
#include "pgstat.h"

#include "spock_apply.h"
#if PG_VERSION_NUM >= 180000
#include "spock_conflict_stat.h"
#endif
#include "spock_executor.h"
#include "spock_node.h"
#include "spock_conflict.h"
Expand Down Expand Up @@ -1224,4 +1227,9 @@ _PG_init(void)

/* Security label provider hook */
register_label_provider(SPOCK_SECLABEL_PROVIDER, spock_object_relabel);

#if PG_VERSION_NUM >= 180000
/* Spock replication conflict statistics */
spock_stat_register_conflict_stat();
#endif
}
13 changes: 13 additions & 0 deletions src/spock_apply_heap.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@

#include "spock_common.h"
#include "spock_conflict.h"
#if PG_VERSION_NUM >= 180000
#include "spock_conflict_stat.h"
#endif
#include "spock_executor.h"
#include "spock_node.h"
#include "spock_proto_native.h"
Expand Down Expand Up @@ -1067,6 +1070,16 @@ spock_apply_heap_update(SpockRelation *rel, SpockTupleData *oldtup,
/* SPOCK_CT_UPDATE_MISSING case gets logged in exception_log, not resolutions */
SpockExceptionLog *exception_log = &exception_log_ptr[my_exception_log_index];

#if PG_VERSION_NUM >= 180000
if (!MyApplyWorker->use_try_block)
/*
* To avoid duplicate messages, only report the conflict on the
* successful pathway. We skip counting when the update logic has
* already failed because the conflict would be misleading.
*/
spock_stat_report_subscription_conflict(MyApplyWorker->subid,
SPOCK_CT_UPDATE_MISSING);
#endif
/*
* The tuple to be updated could not be found. Do nothing except for
* emitting a log message. TODO: Add pkey information as well.
Expand Down
12 changes: 12 additions & 0 deletions src/spock_conflict.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@

#include "spock.h"
#include "spock_conflict.h"
#if PG_VERSION_NUM >= 180000
#include "spock_conflict_stat.h"
#endif
#include "spock_proto_native.h"
#include "spock_node.h"
#include "spock_worker.h"
Expand Down Expand Up @@ -372,6 +375,15 @@ spock_report_conflict(SpockConflictType conflict_type,
handle_stats_counter(rel->rel, MyApplyWorker->subid,
SPOCK_STATS_CONFLICT_COUNT, 1);

#if PG_VERSION_NUM >= 180000
/*
* TODO: Can't enable until SPOCK_CT_DELETE_LATE is either included in
* SPOCK_CONFLICT_NUM_TYPES or filtered out here — passing it as-is would
* overflow the conflict_count[] array.
*
* spock_stat_report_subscription_conflict(MyApplyWorker->subid, conflict_type);
*/
#endif
/* If configured log resolution to spock.resolutions table */
spock_conflict_log_table(conflict_type, rel, localtuple, oldkey,
remotetuple, applytuple, resolution,
Expand Down
Loading