Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,11 @@ jobs:
- { name: stage7-marker-async, ranges: "363-363", unit: false, regress: false }
# t/365 spec-7.1a cross-instance write-write MVCC coordination.
- { name: stage7-write-write, ranges: "365-365", unit: false, regress: false }
# t/366 spec-7.2a GCS block dedup capacity + eager reclaim. Own shard:
# 2-node shared_catalog bring-up + cross-node distinct-read dedup
# pressure + injected retransmit-dedup correctness needs the wall
# clock. (t/364 reserved by the parallel spec-7.3 LMS-pool lane.)
- { name: stage7-gcs-dedup-capacity, ranges: "366-366", unit: false, regress: false }
steps:
- name: Checkout
uses: actions/checkout@v4
Expand Down
45 changes: 9 additions & 36 deletions src/backend/cluster/cluster_catalog_bootstrap.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
*/
#include "postgres.h"

#include <ctype.h>
#include <sys/stat.h>

#include "access/transam.h"
Expand All @@ -44,6 +43,7 @@
#include "cluster/cluster_catalog_bootstrap.h"
#include "cluster/cluster_catalog_migrate.h"
#include "cluster/cluster_cf_authority.h"
#include "cluster/cluster_conf.h"
#include "cluster/cluster_guc.h"
#include "cluster/cluster_inject.h"
#include "cluster/cluster_mode.h"
Expand Down Expand Up @@ -76,40 +76,6 @@ cluster_catalog_xid_authority_corrupt_fatal(const char *detail)
}


/*
* Early D6 topology sniff: cluster_conf_load() runs later when shmem exists,
* but shared_catalog bootstrap must reject multi-node xid_striping=off before
* it seeds/adopts any authority. This deliberately counts only [node.N]
* section headers; the real parser remains the startup SSOT and will still
* validate syntax, required fields, and node_id consistency later.
*/
static int
cluster_catalog_declared_node_count_early(void)
{
const char *path = (cluster_config_file != NULL && cluster_config_file[0] != '\0')
? cluster_config_file
: "pgrac.conf";
FILE *f;
char line[1024];
int nodes = 0;

f = AllocateFile(path, "r");
if (f == NULL)
return 1;

while (fgets(line, sizeof(line), f) != NULL) {
const char *p = line;

while (*p != '\0' && isspace((unsigned char)*p))
p++;
if (strncmp(p, "[node.", 6) == 0)
nodes++;
}
FreeFile(f);

return nodes > 0 ? nodes : 1;
}

static void
cluster_catalog_vet_xid_striping_for_shared_catalog(void)
{
Expand All @@ -118,7 +84,14 @@ cluster_catalog_vet_xid_striping_for_shared_catalog(void)
if (!cluster_enabled || cluster_xid_striping)
return;

nodes = cluster_catalog_declared_node_count_early();
/*
* Early D6 topology sniff: cluster_conf_load() runs later when shmem
* exists, but shared_catalog bootstrap must reject multi-node
* xid_striping=off before it seeds/adopts any authority. The sniff
* helper lives in cluster_conf.c (spec-7.2a hoisted it for a second
* pre-shmem consumer).
*/
nodes = cluster_conf_declared_node_count_early();
if (nodes > 1)
ereport(
FATAL,
Expand Down
47 changes: 47 additions & 0 deletions src/backend/cluster/cluster_conf.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,13 @@
*/
#include "postgres.h"

#include <ctype.h>
#include <sys/stat.h>

#include "fmgr.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "storage/fd.h"
#include "storage/shmem.h"
#include "utils/builtins.h"
#include "utils/tuplestore.h"
Expand Down Expand Up @@ -192,6 +194,51 @@ cluster_conf_node_count(void)
return ClusterConfShmem->node_count;
}

/*
* cluster_conf_declared_node_count_early
*
* Pre-shmem topology sniff (see cluster_conf.h). Deliberately counts only
* [node.N] section headers; cluster_conf_load() remains the startup SSOT
* and still validates syntax, required fields, and node_id consistency
* later. Cached per process: the file is postmaster-static for the life
* of the instance, and shmem size_fn/init_fn must see the same value so
* the reserved region always covers the initialised HTAB (spec-7.2a D4).
* Originally a static helper in cluster_catalog_bootstrap.c (spec-6.14 D6);
* hoisted here when spec-7.2a added a second pre-shmem consumer.
*/
int
cluster_conf_declared_node_count_early(void)
{
static int declared_nodes_cache = -1;
const char *path;
FILE *f;
char line[1024];
int nodes = 0;

if (declared_nodes_cache > 0)
return declared_nodes_cache;

path = (cluster_config_file != NULL && cluster_config_file[0] != '\0') ? cluster_config_file
: "pgrac.conf";

f = AllocateFile(path, "r");
if (f == NULL)
return 1; /* absent file → single-node fallback; do not cache ENOENT */

while (fgets(line, sizeof(line), f) != NULL) {
const char *p = line;

while (*p != '\0' && isspace((unsigned char)*p))
p++;
if (strncmp(p, "[node.", 6) == 0)
nodes++;
}
FreeFile(f);

declared_nodes_cache = nodes > 0 ? nodes : 1;
return declared_nodes_cache;
}


/* ============================================================
* INI parser.
Expand Down
9 changes: 9 additions & 0 deletions src/backend/cluster/cluster_debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -1938,6 +1938,15 @@ dump_gcs(ReturnSetInfo *rsinfo)
fmt_int64((int64)cluster_gcs_get_block_dedup_collision_count()));
emit_row(rsinfo, "gcs", "dedup_full_count",
fmt_int64((int64)cluster_gcs_get_block_dedup_full_count()));
/* spec-7.2a D5: 3 NEW dedup capacity/occupancy rows. entry_count /
* max_entries give the saturation ratio; evict_count counts eager
* reclaim + TTL-sweep removals (dump_gcs gcs-category 85 -> 88). */
emit_row(rsinfo, "gcs", "dedup_entry_count",
fmt_int64((int64)cluster_gcs_get_block_dedup_entry_count()));
emit_row(rsinfo, "gcs", "dedup_evict_count",
fmt_int64((int64)cluster_gcs_get_block_dedup_evict_count()));
emit_row(rsinfo, "gcs", "dedup_max_entries",
fmt_int64((int64)cluster_gcs_get_block_dedup_max_entries()));
emit_row(rsinfo, "gcs", "epoch_invalidate_wake_count",
fmt_int64((int64)cluster_gcs_get_block_epoch_invalidate_wake_count()));
emit_row(rsinfo, "gcs", "stale_reply_drop_count",
Expand Down
57 changes: 50 additions & 7 deletions src/backend/cluster/cluster_gcs_block.c
Original file line number Diff line number Diff line change
Expand Up @@ -4652,13 +4652,31 @@ build_and_send_reply: {
* active on that retry). Useful for driving the
* retransmit_send_count + dedup_hit_count TAP surfaces.
*/
CLUSTER_INJECTION_POINT("cluster-gcs-block-drop-reply-before-send");
if (cluster_injection_should_skip("cluster-gcs-block-drop-reply-before-send")) {
gcs_block_release_ship_image(block_payload_release_cb, block_payload_release_arg);
block_payload_release_cb = NULL;
block_payload_release_arg = NULL;
pfree(buf);
return;
/*
* spec-7.2a: gate the drop-reply dispatch on the test target relfilenode.
* A :skipn:N count is per-process global; without this gate an unrelated
* (catalog / internal) block ship consumes the countdown before the test's
* user-relation ship reaches the point. Gating the CLUSTER_INJECTION_POINT
* itself (not just should_skip) ensures only matching ships consume the
* count. 0 (default) keeps the un-targeted behaviour for spec-2.34 tests.
*
* Current TAP coverage uses target=0 only (shared_catalog remaps the
* catalog-visible relfilenode to a different physical relNumber, so SQL
* cannot name the shipped block); the non-zero filter is reserved for
* precise spec-2.34-style targeting on non-shared-catalog rigs and is not
* yet exercised by any test.
*/
if (cluster_gcs_block_drop_target_relfilenode == 0
|| BufTagGetRelNumber(&req->tag)
== (RelFileNumber)cluster_gcs_block_drop_target_relfilenode) {
CLUSTER_INJECTION_POINT("cluster-gcs-block-drop-reply-before-send");
if (cluster_injection_should_skip("cluster-gcs-block-drop-reply-before-send")) {
gcs_block_release_ship_image(block_payload_release_cb, block_payload_release_arg);
block_payload_release_cb = NULL;
block_payload_release_arg = NULL;
pfree(buf);
return;
}
}

{
Expand Down Expand Up @@ -7070,6 +7088,31 @@ cluster_gcs_get_block_dedup_full_count(void)
return cluster_gcs_block_dedup_get_full_count();
}

/*
* spec-7.2a D5: dedup capacity/occupancy observability wrappers. The
* entry_count wrapper reads the historical _get_in_flight_count accessor,
* whose backing counter (entry_count) tracks every live entry (in-flight
* slots plus completed cached replies) -- that live total is what dump_gcs
* surfaces as dedup_entry_count for the saturation ratio.
*/
uint64
cluster_gcs_get_block_dedup_entry_count(void)
{
return cluster_gcs_block_dedup_get_in_flight_count();
}

uint64
cluster_gcs_get_block_dedup_evict_count(void)
{
return cluster_gcs_block_dedup_get_evict_count();
}

uint64
cluster_gcs_get_block_dedup_max_entries(void)
{
return cluster_gcs_block_dedup_get_max_entries();
}


/* ============================================================
* PGRAC: spec-2.34 D4 — eager wake on epoch advance.
Expand Down
Loading
Loading