Skip to content

Commit 0f54745

Browse files
author
SqlRush
committed
merge: integrate Stage 8 two-stage PRE checkpoint
2 parents 1358c12 + 66ea74f commit 0f54745

69 files changed

Lines changed: 28341 additions & 3080 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/backend/cluster/cluster_cf_phase2.c

Lines changed: 369 additions & 103 deletions
Large diffs are not rendered by default.

src/backend/cluster/cluster_clean_leave.c

Lines changed: 1744 additions & 0 deletions
Large diffs are not rendered by default.

src/backend/cluster/cluster_clean_leave_policy.c

Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,19 @@ cluster_clean_leave_announce_payload_valid(const ClusterLeaveAnnouncePayload *p)
430430
* known kind (an unknown value fails closed like a bad version). */
431431
if (p->producer_kind > CLUSTER_LEAVE_PRODUCER_SHUTDOWN)
432432
return false;
433+
if (p->preflight > CLUSTER_PHASE1_FULL_STOP_WIRE_RECEIPT)
434+
return false;
435+
if (p->preflight >= CLUSTER_PHASE1_FULL_STOP_WIRE_RELEASE
436+
&& (p->producer_kind != CLUSTER_LEAVE_PRODUCER_SHUTDOWN
437+
|| p->_pad0 != 0
438+
|| p->_pad1[0] != 0
439+
|| p->_pad1[1] != 0
440+
|| p->leaving_node_id >= CLUSTER_PHASE1_FULL_STOP_MEMBER_COUNT
441+
|| p->leave_epoch != 0
442+
|| p->cssd_dead_generation != 0
443+
|| p->leave_nonce == 0
444+
|| p->leave_nonce == UINT64_MAX))
445+
return false;
433446
return true;
434447
}
435448

@@ -464,5 +477,263 @@ cluster_clean_leave_ack_payload_valid(const ClusterLeaveAckPayload *p)
464477
return false;
465478
if (p->leaving_node_id < 0 || p->leaving_node_id >= CLUSTER_CLEAN_LEAVE_MAX_NODE_ID)
466479
return false;
480+
if (p->phase1_round != 0
481+
&& p->phase1_round != CLUSTER_PHASE1_FULL_STOP_WIRE_RELEASE)
482+
return false;
483+
if (p->phase1_round == CLUSTER_PHASE1_FULL_STOP_WIRE_RELEASE
484+
&& (p->_pad0 != 0
485+
|| p->_pad1 != 0
486+
|| p->survivor_node_id >= CLUSTER_PHASE1_FULL_STOP_MEMBER_COUNT
487+
|| p->leaving_node_id >= CLUSTER_PHASE1_FULL_STOP_MEMBER_COUNT
488+
|| p->survivor_node_id == p->leaving_node_id
489+
|| p->nak != 0
490+
|| p->nak_reason != CLUSTER_LEAVE_NAK_NONE
491+
|| p->leave_epoch != 0
492+
|| p->leave_nonce == 0
493+
|| p->leave_nonce == UINT64_MAX))
494+
return false;
467495
return true;
468496
}
497+
498+
/* ------------------------------------------------------------------
499+
* Stage 8 phase-1 coordinated full-cluster clean-stop policy.
500+
* ------------------------------------------------------------------ */
501+
502+
bool
503+
cluster_clean_leave_phase1_full_stop_plan_valid(
504+
const ClusterPhase1FullStopPlan *plan)
505+
{
506+
int i;
507+
508+
if (plan == NULL || !plan->valid || plan->epoch != 0
509+
|| plan->attempt_nonce == 0 || plan->attempt_nonce == UINT64_MAX
510+
|| plan->absolute_deadline_us == 0
511+
|| plan->absolute_deadline_us == UINT64_MAX
512+
|| plan->own_wal_started_at <= 0)
513+
return false;
514+
for (i = 0; i < CLUSTER_PHASE1_FULL_STOP_MEMBER_COUNT; i++) {
515+
if (plan->member_incarnations[i] == 0
516+
|| plan->member_incarnations[i] == UINT64_MAX)
517+
return false;
518+
}
519+
return true;
520+
}
521+
522+
bool
523+
cluster_clean_leave_phase1_full_stop_probe_accepts(
524+
uint8 producer_kind, bool preflight, int32 envelope_source_node,
525+
int32 payload_leaving_node, uint64 envelope_epoch, uint64 payload_epoch,
526+
uint64 attempt_nonce, bool local_fast_shutdown, bool exact_phase1_eligible)
527+
{
528+
return producer_kind == CLUSTER_LEAVE_PRODUCER_SHUTDOWN
529+
&& preflight
530+
&& envelope_source_node >= 0
531+
&& envelope_source_node < CLUSTER_PHASE1_FULL_STOP_MEMBER_COUNT
532+
&& envelope_source_node == payload_leaving_node
533+
&& envelope_epoch == payload_epoch
534+
&& payload_epoch == 0
535+
&& attempt_nonce != 0 && attempt_nonce != UINT64_MAX
536+
&& local_fast_shutdown && exact_phase1_eligible;
537+
}
538+
539+
bool
540+
cluster_clean_leave_phase1_full_stop_probe_phase_accepts(
541+
bool source_active, bool source_stopped,
542+
bool local_active, bool local_stopped)
543+
{
544+
/* The two rounds deliberately reuse one wire shape. Existing source
545+
* WAL/root evidence is therefore the phase discriminator: an ACTIVE source
546+
* is still in the first round, while STOPPED+CLOSED is the second round.
547+
* A receiver may already have closed while helping a slower first-round
548+
* peer, but it must never ACK a second-round peer before its own close. */
549+
if (source_active == source_stopped
550+
|| local_active == local_stopped)
551+
return false;
552+
if (source_stopped)
553+
return local_stopped;
554+
return source_active;
555+
}
556+
557+
bool
558+
cluster_clean_leave_phase1_full_stop_post_stopped_receiver_ready(
559+
bool local_stopped, bool request_in_progress, bool shutdown_driven,
560+
bool preflight_pending, bool preflight_sent, bool release_pending)
561+
{
562+
/* The STOPPED after-image becomes an acknowledged local terminal only
563+
* after its publisher has confirmed the CF release and armed this process's
564+
* second round. Requiring the existing dispatch edge as well prevents a
565+
* peer from exiting before it has had a chance to receive our request. A
566+
* locally completed barrier remains the same exact round while its bounded
567+
* release/completion phase is active; accepting a slower peer's replay in
568+
* that interval prevents a one-way completion race without reopening the
569+
* round or refreshing its deadline. */
570+
return local_stopped && request_in_progress && shutdown_driven
571+
&& preflight_sent && (preflight_pending || release_pending);
572+
}
573+
574+
bool
575+
cluster_clean_leave_phase1_full_stop_release_probe_accepts(
576+
uint8 producer_kind, uint8 wire_round, int32 envelope_source_node,
577+
int32 payload_leaving_node, uint64 envelope_epoch, uint64 payload_epoch,
578+
uint64 nonce, bool source_stopped, bool local_stopped,
579+
bool exact_phase1_eligible)
580+
{
581+
return producer_kind == CLUSTER_LEAVE_PRODUCER_SHUTDOWN
582+
&& wire_round == CLUSTER_PHASE1_FULL_STOP_WIRE_RELEASE
583+
&& envelope_source_node >= 0
584+
&& envelope_source_node < CLUSTER_PHASE1_FULL_STOP_MEMBER_COUNT
585+
&& envelope_source_node == payload_leaving_node
586+
&& envelope_epoch == payload_epoch
587+
&& payload_epoch == 0
588+
&& nonce != 0 && nonce != UINT64_MAX
589+
&& source_stopped && local_stopped && exact_phase1_eligible;
590+
}
591+
592+
bool
593+
cluster_clean_leave_phase1_full_stop_receipt_accepts(
594+
uint8 producer_kind, uint8 wire_round, bool release_pending,
595+
uint64 stored_nonce, uint64 receipt_nonce, bool request_seen)
596+
{
597+
/* The exact receipt is peer-consumption evidence for a reply whose local
598+
* reply_sent publication may still be catching up after dispatch. Do not
599+
* require that local bookkeeping bit here: the terminal predicate checks it
600+
* independently together with every other delivery bit and transport drain. */
601+
return producer_kind == CLUSTER_LEAVE_PRODUCER_SHUTDOWN
602+
&& wire_round == CLUSTER_PHASE1_FULL_STOP_WIRE_RECEIPT
603+
&& release_pending
604+
&& stored_nonce != 0 && stored_nonce != UINT64_MAX
605+
&& receipt_nonce == stored_nonce
606+
&& request_seen;
607+
}
608+
609+
bool
610+
cluster_clean_leave_phase1_full_stop_release_complete(
611+
const ClusterPhase1FullStopPlan *plan, int32 self_node,
612+
const uint8 *request_sent, const uint8 *request_seen,
613+
const uint8 *reply_sent, const uint8 *reply_seen,
614+
const uint8 *receipt_sent, const uint8 *receipt_seen,
615+
int nbytes, bool transport_drained)
616+
{
617+
return transport_drained
618+
&& cluster_clean_leave_phase1_full_stop_ack_complete(
619+
plan, self_node, request_sent, nbytes)
620+
&& cluster_clean_leave_phase1_full_stop_ack_complete(
621+
plan, self_node, request_seen, nbytes)
622+
&& cluster_clean_leave_phase1_full_stop_ack_complete(
623+
plan, self_node, reply_sent, nbytes)
624+
&& cluster_clean_leave_phase1_full_stop_ack_complete(
625+
plan, self_node, reply_seen, nbytes)
626+
&& cluster_clean_leave_phase1_full_stop_ack_complete(
627+
plan, self_node, receipt_sent, nbytes)
628+
&& cluster_clean_leave_phase1_full_stop_ack_complete(
629+
plan, self_node, receipt_seen, nbytes);
630+
}
631+
632+
bool
633+
cluster_clean_leave_phase1_full_stop_request_ahead_can_consume(
634+
bool retained, bool retained_before_local_round,
635+
uint64 retained_local_nonce, uint64 retained_deadline_us,
636+
uint64 current_local_nonce, uint64 current_deadline_us,
637+
bool exact_identity, bool request_in_progress,
638+
bool shutdown_driven, bool post_requests_sent)
639+
{
640+
if (!retained || !exact_identity || !request_in_progress
641+
|| !shutdown_driven || !post_requests_sent
642+
|| retained_local_nonce == 0
643+
|| retained_local_nonce == UINT64_MAX
644+
|| retained_deadline_us == 0
645+
|| retained_deadline_us == UINT64_MAX
646+
|| current_local_nonce == 0
647+
|| current_local_nonce == UINT64_MAX
648+
|| current_deadline_us != retained_deadline_us)
649+
return false;
650+
if (retained_before_local_round)
651+
return cluster_clean_leave_phase1_full_stop_nonce_fresh(
652+
retained_local_nonce, current_local_nonce);
653+
return current_local_nonce == retained_local_nonce;
654+
}
655+
656+
bool
657+
cluster_clean_leave_phase1_full_stop_request_ahead_uses_predecessor_nonce(
658+
bool local_active, bool local_stopped, bool local_post_round_armed)
659+
{
660+
/* WAL STOPPED is published before the checkpointer freezes the fresh
661+
* post-STOPPED nonce. That interval still belongs to the predecessor
662+
* local round even though the physical WAL after-image is already STOPPED.
663+
* Once the post round is armed, an early peer request binds the current
664+
* nonce and must not authorize another transition. */
665+
if (local_active == local_stopped)
666+
return false;
667+
return local_active || (local_stopped && !local_post_round_armed);
668+
}
669+
670+
bool
671+
cluster_clean_leave_phase1_full_stop_ack_matches(
672+
const ClusterPhase1FullStopPlan *plan, int32 self_node,
673+
int32 envelope_source_node, int32 survivor_node, int32 leaving_node,
674+
uint64 payload_epoch, uint64 attempt_nonce,
675+
uint64 current_survivor_incarnation)
676+
{
677+
return cluster_clean_leave_phase1_full_stop_plan_valid(plan)
678+
&& self_node >= 0
679+
&& self_node < CLUSTER_PHASE1_FULL_STOP_MEMBER_COUNT
680+
&& survivor_node >= 0
681+
&& survivor_node < CLUSTER_PHASE1_FULL_STOP_MEMBER_COUNT
682+
&& survivor_node != self_node
683+
&& envelope_source_node == survivor_node
684+
&& leaving_node == self_node
685+
&& payload_epoch == plan->epoch
686+
&& attempt_nonce == plan->attempt_nonce
687+
&& current_survivor_incarnation
688+
== plan->member_incarnations[survivor_node];
689+
}
690+
691+
bool
692+
cluster_clean_leave_phase1_full_stop_ack_complete(
693+
const ClusterPhase1FullStopPlan *plan, int32 self_node,
694+
const uint8 *ack_bitmap, int nbytes)
695+
{
696+
uint8 expected;
697+
int i;
698+
699+
if (!cluster_clean_leave_phase1_full_stop_plan_valid(plan)
700+
|| self_node < 0
701+
|| self_node >= CLUSTER_PHASE1_FULL_STOP_MEMBER_COUNT
702+
|| ack_bitmap == NULL
703+
|| nbytes != CLUSTER_CLEAN_LEAVE_ACK_BITMAP_BYTES)
704+
return false;
705+
expected = (uint8)(UINT8_C(0x0f) & ~(UINT8_C(1) << self_node));
706+
if (ack_bitmap[0] != expected)
707+
return false;
708+
for (i = 1; i < nbytes; i++) {
709+
if (ack_bitmap[i] != 0)
710+
return false;
711+
}
712+
return true;
713+
}
714+
715+
bool
716+
cluster_clean_leave_phase1_full_stop_nonce_fresh(uint64 prior_nonce,
717+
uint64 next_nonce)
718+
{
719+
return prior_nonce != 0 && prior_nonce != UINT64_MAX
720+
&& next_nonce != 0 && next_nonce != UINT64_MAX
721+
&& next_nonce != prior_nonce;
722+
}
723+
724+
ClusterPhase1FullStopProbeNonceDecision
725+
cluster_clean_leave_phase1_full_stop_probe_nonce_decide(
726+
uint64 active_nonce, uint64 stopped_nonce, uint64 incoming_nonce)
727+
{
728+
if (active_nonce == 0 || active_nonce == UINT64_MAX
729+
|| stopped_nonce == UINT64_MAX
730+
|| incoming_nonce == 0 || incoming_nonce == UINT64_MAX)
731+
return CLUSTER_PHASE1_PROBE_NONCE_CONFLICT;
732+
if (incoming_nonce == active_nonce)
733+
return CLUSTER_PHASE1_PROBE_NONCE_STALE_ACTIVE;
734+
if (stopped_nonce == 0)
735+
return CLUSTER_PHASE1_PROBE_NONCE_ACCEPT_STOPPED;
736+
if (incoming_nonce == stopped_nonce)
737+
return CLUSTER_PHASE1_PROBE_NONCE_DUPLICATE_STOPPED;
738+
return CLUSTER_PHASE1_PROBE_NONCE_CONFLICT;
739+
}

0 commit comments

Comments
 (0)