Skip to content

Commit c3d10b7

Browse files
authored
Merge pull request #61 from sqlrush/fix/s3-preflight-runtime-gate
fix(cluster): close S3 preflight runtime gate
2 parents c0293c6 + 06d2446 commit c3d10b7

9 files changed

Lines changed: 74 additions & 21 deletions

File tree

src/backend/cluster/cluster_debug.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2411,6 +2411,8 @@ dump_gcs(ReturnSetInfo *rsinfo)
24112411
/* PGRAC: spec-4.7 D6 — 8 NEW counter rows for GCS/PCM warm recovery. */
24122412
emit_row(rsinfo, "gcs_recovery", "block_resources_recovering",
24132413
fmt_int64((int64)cluster_gcs_get_recovery_block_resources_recovering()));
2414+
emit_row(rsinfo, "gcs_recovery", "pcm_x_image_fetch_recovering_retry_count",
2415+
fmt_int64((int64)cluster_gcs_get_pcm_x_image_fetch_recovering_retry_count()));
24142416
emit_row(rsinfo, "gcs_recovery", "buffers_redeclared",
24152417
fmt_int64((int64)cluster_gcs_get_recovery_buffers_redeclared()));
24162418
emit_row(rsinfo, "gcs_recovery", "block_state_rebuilt",

src/backend/cluster/cluster_gcs_block.c

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -297,13 +297,16 @@ typedef struct ClusterGcsBlockShared {
297297
clean_page_xfer_stale_holder_recover_count; /* RESERVED Stage 6 (was DENIED recover) */
298298
pg_atomic_uint64 clean_page_xfer_third_party_denied_count; /* 3-node third-party master DENY */
299299
/* PGRAC: spec-4.7 D6 — GCS/PCM warm-recovery observability (dump category
300-
* 'gcs_recovery'). 8 counters per spec §2.4. */
300+
* 'gcs_recovery'). */
301301
pg_atomic_uint64 recovery_block_resources_recovering; /* phase_for_tag → RECOVERING hits */
302-
pg_atomic_uint64 recovery_buffers_redeclared; /* survivor re-declare sent (D2) */
303-
pg_atomic_uint64 recovery_block_state_rebuilt; /* master rebuild applied (D2/D3) */
304-
pg_atomic_uint64 recovery_redo_boundary_waits; /* redo gate: not yet covered (D5) */
305-
pg_atomic_uint64 recovery_redo_boundary_reached; /* redo gate: covered (D5) */
306-
pg_atomic_uint64 recovery_stale_block_drop; /* re-declare dropped: off-epoch/bad (D2) */
302+
/* PCM-X requester received a retryable RESOURCE_RECOVERING denial while
303+
* fetching its generation-exact holder image. */
304+
pg_atomic_uint64 pcm_x_image_fetch_recovering_retry_count;
305+
pg_atomic_uint64 recovery_buffers_redeclared; /* survivor re-declare sent (D2) */
306+
pg_atomic_uint64 recovery_block_state_rebuilt; /* master rebuild applied (D2/D3) */
307+
pg_atomic_uint64 recovery_redo_boundary_waits; /* redo gate: not yet covered (D5) */
308+
pg_atomic_uint64 recovery_redo_boundary_reached; /* redo gate: covered (D5) */
309+
pg_atomic_uint64 recovery_stale_block_drop; /* re-declare dropped: off-epoch/bad (D2) */
307310
pg_atomic_uint64 recovery_ambiguous_owner_failclosed; /* not-double-X conflict (D3) */
308311
pg_atomic_uint64 recovery_before_boundary_failclosed; /* served-before-redo gate fail (D5) */
309312
/* PGRAC: spec-2.36 D3 (HC116) — master broadcast invalidate slot.
@@ -600,6 +603,7 @@ cluster_gcs_block_shmem_init(void)
600603
pg_atomic_init_u64(&ClusterGcsBlock->clean_page_xfer_third_party_denied_count, 0);
601604
/* PGRAC: spec-4.7 D6 — 8 NEW warm-recovery counters init. */
602605
pg_atomic_init_u64(&ClusterGcsBlock->recovery_block_resources_recovering, 0);
606+
pg_atomic_init_u64(&ClusterGcsBlock->pcm_x_image_fetch_recovering_retry_count, 0);
603607
pg_atomic_init_u64(&ClusterGcsBlock->recovery_buffers_redeclared, 0);
604608
pg_atomic_init_u64(&ClusterGcsBlock->recovery_block_state_rebuilt, 0);
605609
pg_atomic_init_u64(&ClusterGcsBlock->recovery_redo_boundary_waits, 0);
@@ -3379,6 +3383,9 @@ cluster_gcs_pcm_x_fetch_image_and_install(BufferDesc *buf, const PcmXLocalHandle
33793383
|| reply.status == (uint8)GCS_BLOCK_REPLY_DENIED_RESOURCE_RECOVERING) {
33803384
if (reply.status == (uint8)GCS_BLOCK_REPLY_DENIED_MASTER_NOT_HOLDER)
33813385
pg_atomic_fetch_add_u64(&ClusterGcsBlock->block_master_not_holder_count, 1);
3386+
else
3387+
pg_atomic_fetch_add_u64(
3388+
&ClusterGcsBlock->pcm_x_image_fetch_recovering_retry_count, 1);
33823389
continue;
33833390
}
33843391
if (!cluster_pcm_x_image_fetch_reply_exact(&reply, reply_block, &progress_now,
@@ -14480,6 +14487,14 @@ cluster_gcs_get_recovery_block_resources_recovering(void)
1448014487
? pg_atomic_read_u64(&ClusterGcsBlock->recovery_block_resources_recovering)
1448114488
: 0;
1448214489
}
14490+
14491+
uint64
14492+
cluster_gcs_get_pcm_x_image_fetch_recovering_retry_count(void)
14493+
{
14494+
return ClusterGcsBlock
14495+
? pg_atomic_read_u64(&ClusterGcsBlock->pcm_x_image_fetch_recovering_retry_count)
14496+
: 0;
14497+
}
1448314498
uint64
1448414499
cluster_gcs_get_recovery_buffers_redeclared(void)
1448514500
{

src/backend/storage/buffer/bufmgr.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8427,9 +8427,10 @@ ConditionalLockBuffer(Buffer buffer)
84278427
* GRANT_PENDING must not modify protocol-owned bytes.
84288428
*/
84298429
buf_state = LockBufHdr(buf);
8430-
blocked = cluster_bufmgr_pcm_x_retained_image_locked(buf, buf_state)
8431-
|| buf->pcm_state != (uint8) PCM_STATE_X
8432-
|| cluster_pcm_own_flags_get(buf->buf_id) != 0;
8430+
blocked = !cluster_pcm_x_conditional_lock_allowed(
8431+
cluster_pcm_is_active(), cluster_bufmgr_should_pcm_track(buf),
8432+
cluster_bufmgr_pcm_x_retained_image_locked(buf, buf_state), buf->pcm_state,
8433+
cluster_pcm_own_flags_get(buf->buf_id));
84338434
UnlockBufHdr(buf, buf_state);
84348435
if (blocked)
84358436
{

src/include/cluster/cluster_gcs_block.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3513,6 +3513,7 @@ extern void cluster_gcs_block_send_pi_discard_invalidate(BufferTag tag, int32 ta
35133513

35143514
/* PGRAC: spec-4.7 D6 — 8 warm-recovery observability accessors. */
35153515
extern uint64 cluster_gcs_get_recovery_block_resources_recovering(void);
3516+
extern uint64 cluster_gcs_get_pcm_x_image_fetch_recovering_retry_count(void);
35163517
extern uint64 cluster_gcs_get_recovery_buffers_redeclared(void);
35173518
extern uint64 cluster_gcs_get_recovery_block_state_rebuilt(void);
35183519
extern uint64 cluster_gcs_get_recovery_redo_boundary_waits(void);

src/include/cluster/cluster_pcm_x_bufmgr.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,18 @@ cluster_pcm_x_cached_cover_bypasses_queue(bool local_cache, bool requested_x, ui
165165
return local_cache && requested_x && pcm_state == (uint8)PCM_STATE_X && flags == 0;
166166
}
167167

168+
/* ConditionalLockBuffer cannot initiate a PCM conversion. Preserve native
169+
* PostgreSQL behavior while PCM is inactive and for relations outside the
170+
* coherence domain; an active tracked page must already hold exact X. Live
171+
* transition/retained evidence remains closed regardless of runtime state. */
172+
static inline bool
173+
cluster_pcm_x_conditional_lock_allowed(bool runtime_active, bool tracked, bool retained_image,
174+
uint8 pcm_state, uint32 flags)
175+
{
176+
return !retained_image && flags == 0
177+
&& (!runtime_active || !tracked || pcm_state == (uint8)PCM_STATE_X);
178+
}
179+
168180
typedef ClusterPcmOwnSnapshot ClusterPcmOwnEvictionCapture;
169181

170182
typedef enum ClusterPcmXGrantReservationKind {

src/test/cluster_tap/t/251_gcs_pcm_warm_recovery.pl

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969
# L2/L3 (sig b/c) DEFERRED to D1-D5 acceptance + cluster_unit (L239,
7070
# see finding above). SKIPed here with reason.
7171
# Lobs (D6/observability, FLIPPED) gcs_recovery dump category exposes the
72-
# 8 warm-recovery counters under category='gcs_recovery'.
72+
# warm-recovery counters under category='gcs_recovery'.
7373
#
7474
# Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7575
# Portions Copyright (c) 1994, Regents of the University of California
@@ -215,19 +215,19 @@
215215
# 8 warm-recovery counters: block_resources_recovering / buffers_redeclared /
216216
# block_state_rebuilt / redo_boundary_waits / redo_boundary_reached /
217217
# stale_block_drop / ambiguous_owner_failclosed / before_boundary_failclosed).
218-
# spec-2.41 D7 adds 2 redo-coverage serve-gate counters to the same category
219-
# (redo_coverage_required_lsn_zero_count / redo_coverage_gate_block_count) → 10.
218+
# spec-2.41 D7 adds 2 redo-coverage serve-gate counters to the same category;
219+
# PCM-X adds one image-fetch RESOURCE_RECOVERING retry counter → 11.
220220
# ----------
221221
is($triple->node0->safe_psql('postgres',
222222
q{SELECT count(*) FROM cluster_dump_state()
223223
WHERE category = 'gcs_recovery'}),
224-
'10',
225-
'Lobs (D6 flipped + spec-2.41 D7): gcs_recovery dump category exposes 10 '
224+
'11',
225+
'Lobs (D6 flipped + spec-2.41 D7 + PCM-X): gcs_recovery dump category exposes 11 '
226226
. 'counters — 8 warm-recovery (block_resources_recovering / buffers_redeclared / '
227227
. 'block_state_rebuilt / redo_boundary_waits / redo_boundary_reached / '
228228
. 'stale_block_drop / ambiguous_owner_failclosed / before_boundary_failclosed) '
229229
. '+ 2 redo-coverage serve-gate (redo_coverage_required_lsn_zero_count / '
230-
. 'redo_coverage_gate_block_count)');
230+
. 'redo_coverage_gate_block_count) + 1 PCM-X image-fetch recovering retry');
231231

232232

233233
$triple->stop_triple;

src/test/cluster_unit/test_cluster_debug.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1650,6 +1650,11 @@ cluster_gcs_get_recovery_block_resources_recovering(void)
16501650
return 0;
16511651
}
16521652
uint64
1653+
cluster_gcs_get_pcm_x_image_fetch_recovering_retry_count(void)
1654+
{
1655+
return 0;
1656+
}
1657+
uint64
16531658
cluster_gcs_get_recovery_buffers_redeclared(void)
16541659
{
16551660
return 0;

src/test/cluster_unit/test_cluster_gcs_block.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2189,6 +2189,7 @@ UT_TEST(test_pcm_x_requester_fetch_revalidates_queue_and_reservation_before_inst
21892189
const char *install_publish;
21902190
const char *install_runtime_after;
21912191
const char *recovering_retry;
2192+
const char *recovering_retry_counter;
21922193
const char *reply_validation;
21932194
char *reply_handler;
21942195

@@ -2208,11 +2209,16 @@ UT_TEST(test_pcm_x_requester_fetch_revalidates_queue_and_reservation_before_inst
22082209
UT_ASSERT_NOT_NULL(strstr(fetch, "gcs_block_pcm_x_install_reserved_image_exact("));
22092210
UT_ASSERT_NOT_NULL(strstr(fetch, "gcs_block_release_slot(slot)"));
22102211
recovering_retry = strstr(fetch, "GCS_BLOCK_REPLY_DENIED_RESOURCE_RECOVERING");
2212+
recovering_retry_counter = strstr(fetch, "pcm_x_image_fetch_recovering_retry_count");
22112213
reply_validation = strstr(fetch, "cluster_pcm_x_image_fetch_reply_exact(");
22122214
UT_ASSERT_NOT_NULL(recovering_retry);
2215+
UT_ASSERT_NOT_NULL(recovering_retry_counter);
22132216
UT_ASSERT(recovering_retry == NULL || recovering_retry < fetch_end);
2217+
UT_ASSERT(recovering_retry_counter == NULL || recovering_retry_counter < fetch_end);
22142218
UT_ASSERT(recovering_retry == NULL || reply_validation == NULL
22152219
|| recovering_retry < reply_validation);
2220+
UT_ASSERT(recovering_retry_counter == NULL || reply_validation == NULL
2221+
|| recovering_retry_counter < reply_validation);
22162222
backoff_branch = strstr(fetch, "if (retry_attempt > 0)");
22172223
backoff_wait
22182224
= backoff_branch != NULL ? strstr(backoff_branch, "(void)WaitLatch(MyLatch") : NULL;

src/test/cluster_unit/test_cluster_pcm_own.c

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1211,17 +1211,15 @@ UT_TEST(test_retained_image_release_and_writeback_gates_are_exact)
12111211
if (conditional != NULL) {
12121212
const char *content
12131213
= strstr(conditional, "LWLockConditionalAcquire(BufferDescriptorGetContentLock(buf)");
1214-
const char *ownership = strstr(conditional, "buf->pcm_state != (uint8) PCM_STATE_X");
1215-
const char *flags = strstr(conditional, "cluster_pcm_own_flags_get(buf->buf_id) != 0");
1214+
const char *ownership = strstr(conditional, "cluster_pcm_x_conditional_lock_allowed(");
12161215
const char *release
12171216
= strstr(conditional, "LWLockRelease(BufferDescriptorGetContentLock(buf))");
12181217

12191218
UT_ASSERT_NOT_NULL(content);
12201219
UT_ASSERT_NOT_NULL(ownership);
1221-
UT_ASSERT_NOT_NULL(flags);
12221220
UT_ASSERT_NOT_NULL(release);
1223-
if (content != NULL && ownership != NULL && flags != NULL && release != NULL)
1224-
UT_ASSERT(content < ownership && ownership < flags && flags < release);
1221+
if (content != NULL && ownership != NULL && release != NULL)
1222+
UT_ASSERT(content < ownership && ownership < release);
12251223
}
12261224
if (resident_stamp != NULL) {
12271225
const char *content = strstr(
@@ -1659,6 +1657,18 @@ UT_TEST(test_current_image_shape_accepts_monotone_xcur_after_x_to_s_yield)
16591657
UT_ASSERT(!cluster_pcm_x_current_image_shape((uint8)PCM_STATE_S, (uint8)BUF_TYPE_XCUR, false));
16601658
}
16611659

1660+
UT_TEST(test_conditional_lock_preserves_native_off_and_enforces_tracked_x)
1661+
{
1662+
UT_ASSERT(cluster_pcm_x_conditional_lock_allowed(false, true, false, (uint8)PCM_STATE_N, 0));
1663+
UT_ASSERT(cluster_pcm_x_conditional_lock_allowed(true, false, false, (uint8)PCM_STATE_N, 0));
1664+
UT_ASSERT(!cluster_pcm_x_conditional_lock_allowed(true, true, false, (uint8)PCM_STATE_N, 0));
1665+
UT_ASSERT(!cluster_pcm_x_conditional_lock_allowed(true, true, false, (uint8)PCM_STATE_S, 0));
1666+
UT_ASSERT(cluster_pcm_x_conditional_lock_allowed(true, true, false, (uint8)PCM_STATE_X, 0));
1667+
UT_ASSERT(!cluster_pcm_x_conditional_lock_allowed(false, false, true, (uint8)PCM_STATE_X, 0));
1668+
UT_ASSERT(!cluster_pcm_x_conditional_lock_allowed(false, false, false, (uint8)PCM_STATE_X,
1669+
PCM_OWN_FLAG_GRANT_PENDING));
1670+
}
1671+
16621672
UT_TEST(test_queue_passive_n_mirror_is_never_gcs_ship_authority)
16631673
{
16641674
static const char *const probe_contract[]
@@ -1977,7 +1987,7 @@ UT_TEST(test_lockbuffer_pcm_x_writer_ledger_is_distinct_and_brackets_content_aut
19771987
int
19781988
main(void)
19791989
{
1980-
UT_PLAN(44);
1990+
UT_PLAN(45);
19811991
UT_RUN(test_shmem_initializes_complete_entry);
19821992
UT_RUN(test_begin_abort_is_exact_and_monotonic);
19831993
UT_RUN(test_invalid_live_flag_shapes_are_corrupt_not_busy);
@@ -2017,6 +2027,7 @@ main(void)
20172027
UT_RUN(test_queue_holder_snapshot_by_tag_is_mapping_and_header_exact);
20182028
UT_RUN(test_queue_passive_pinned_s_release_serializes_bytes_and_ownership);
20192029
UT_RUN(test_current_image_shape_accepts_monotone_xcur_after_x_to_s_yield);
2030+
UT_RUN(test_conditional_lock_preserves_native_off_and_enforces_tracked_x);
20202031
UT_RUN(test_queue_installed_image_publication_is_exact_and_content_locked);
20212032
UT_RUN(test_queue_self_source_handoff_is_single_lifecycle_and_readonly_drain);
20222033
UT_RUN(test_queue_passive_n_mirror_is_never_gcs_ship_authority);

0 commit comments

Comments
 (0)