feat(cluster): GCS block dedup capacity + eager-GC (spec-7.2a)#32
Merged
Conversation
added 7 commits
July 10, 2026 19:55
… tests Add two pure, header-inline decision primitives that carry the eager-reclaim correctness contract for GCS block dedup: - GcsBlockReplyStatusIsReclaimIdempotent(status): in-window reclaim whitelist, default fail-closed (false). Explicit switch so a status is admitted only by an added `case ...: return true;`. Whitelist currently empty. - GcsBlockDedupEntryIsReclaimSafe(entry, now, out_of_window_us): per-entry verdict. in-flight -> never; completed & aged past the out-of-window (2x) -> safe for every status; still in-window -> safe only if the status is whitelisted idempotent. New header-only unit test test_cluster_gcs_block_dedup_reclaim (12 checks, R1-R12): whitelist fail-closed for every enum status, payload/storage-fallback/ pending-x/lost-write/read-image/forward-marker never in-window, in-flight never safe, aged-out safe for all, in-window payload unsafe, strict boundary. Behavioral reclaim path covered in cluster_tap (follow-on). Registered in cluster_unit Makefile (non-simple, links cluster_version.o + common/port srv libs; local ExceptionalCondition stub like siblings). Spec: spec-7.2a-gcs-block-dedup-capacity-gc.md
Wire the eager-reclaim safety primitives into the master-side dedup HTAB so a cap-full request tries to reclaim a reclaim-safe entry before failing closed, and raise the capacity defaults. cluster_gcs_block_dedup.c: - Add evict_count atomic (init + accessor cluster_gcs_block_dedup_get_evict_count). - dedup_reclaim_reclaimable_locked(now, want): bounded probe (GCS_BLOCK_DEDUP_RECLAIM_MAX_PROBE=256, not a full O(cap) scan under the exclusive lock) that removes up to `want` entries GcsBlockDedupEntryIsReclaimSafe approves; hash_seq_term on early break; accounts entry_count-- / evict_count++. - lookup_or_register MISS path: on HASH_ENTER_NULL NULL, reclaim 1 then retry once; still full -> unchanged DENIED_DEDUP_FULL fail-closed (HC92). - TTL sweep also folds removals into evict_count (D5). cluster_guc.c: cluster.gcs_block_dedup_max_entries default 1024->4096, ceiling 16384->65536; description refreshed (entry 8448B, ~34.7MB/~554MB, eager reclaim, PGC_POSTMASTER restart). Reclaim never removes an entry whose retransmitted duplicate could be re-served incorrectly: with the whitelist empty it only removes entries aged past the 2x out-of-window threshold (safe for every status), so it only brings the FULL decision forward in time. Behavioral coverage (cap-full -> reclaim, evict_count, retransmit-dedup correctness) lands in cluster_tap. Spec: spec-7.2a-gcs-block-dedup-capacity-gc.md
…-GC LOG-once + count-based injection + 2-node capacity TAP Observability (D5): - dump_gcs: 3 NEW dedup rows (dedup_entry_count / dedup_evict_count / dedup_max_entries) via cluster_gcs_get_block_dedup_* wrappers - dedup TTL sweep: saturation LOG-once when DENIED_DEDUP_FULL grows past a threshold, lock-free, no hot-path flood Injection framework: - new CLUSTER_FAULT_SKIP_N (skipn:N) count-based skip — drops exactly N times then stops so the sender's retransmit lands; existing skip:N stays unlimited (zero regression, verified by t/215) - test-only GUC cluster.gcs_block_drop_target_relfilenode gates the drop-reply injection to one relation (default 0 = any) Tests: - new t/365_gcs_block_dedup_capacity_2node.pl (L1-L5, shared_catalog 2-node): default cap, cross-node distinct-read dedup_full=0, retransmit path exercised with correct re-serve, reclaim eviction, dump rows - baseline sweep: pg_cluster_state gcs category 85->88 across t/110-116; cluster.gcs_block_dedup_max_entries default 1024->4096 (t/112) - nightly.yml: stage7-gcs-dedup-capacity shard (t/365) Spec: spec-7.2a-gcs-block-dedup-capacity-gc.md
Implement the Q4=A auto-size lower bound: the effective dedup HTAB capacity is raised to MaxConnections x declared node count (clamped at the GUC ceiling) so an undersized cluster.gcs_block_dedup_max_entries cannot saturate under distinct-read pressure. The declared node count cannot come from cluster_conf_node_count(): cluster_conf_load() runs only after every shmem region is initialised, so it still reads 0 at size_fn/init_fn time. Hoist the pre-shmem [node.N] sniff (previously a static helper in the shared-catalog bootstrap) into cluster_conf.c as cluster_conf_declared_node_count_early() with a per-process cache, and consume it from cluster_gcs_block_dedup_effective_entries(). Stamp the effective capacity into the dedup shmem header at init so the D5 observability accessor and the saturation LOG report the size the HTAB was actually built with in every process. Also refresh the stale GUC doc block (1024/16384/8312B -> 4096/65536/8448B) and hoist the ceiling into CLUSTER_GCS_BLOCK_DEDUP_MAX_ENTRIES_CEILING shared by the GUC registration and the floor clamp. Spec: spec-7.2a-gcs-block-dedup-capacity-gc.md
… unit (spec-7.2a review F1/F2/F4) t/365 collides with the write-write TAP already on main under the same number; the dedup capacity test moves to t/366 (nightly shard range, banner self-references, and the unit-side t/NNN placeholders follow). test_cluster_debug gains the 3 missing stubs for the new dedup capacity/occupancy accessors so a clean full cluster_unit build links again. New test_cluster_gcs_block_dedup_htab links the real cluster_gcs_block_dedup.o against a bounded fake HTAB fixture (with HASH_REMOVE + hole reuse and a controlled clock) and drives lookup_or_register to cap: fill-to-cap FULL, in-window retention, out-of-window eager reclaim + re-register conservation, reclaimed-key re-registration, remove accounting, and the D4 auto-size floor legs. The TTL sweep is never called, so evictions are attributable to the eager path alone. Spec: spec-7.2a-gcs-block-dedup-capacity-gc.md
… (spec-7.2a review P3) - skip_remaining field comment said :skip:N; the count-based syntax is :skipn:N (0 now documented as exhausted-or-not-armed). - t/116 L9 label and section header still said 67/48 gcs keys; the assertion is 88. - The drop-reply relfilenode gate comment and GUC description now state that current TAP coverage uses target=0 only (shared_catalog remaps the physical relNumber away from SQL view) and the non-zero filter is reserved for non-shared-catalog rigs. Spec: spec-7.2a-gcs-block-dedup-capacity-gc.md
…84 (spec-7.2a D4) The spec-7.2a S1 perf gate requires the shipped default (no hand bump) to survive the 4-node distinct-read scenario with dedup_full_count = 0 and zero client errors. Measured on the 4-node RACvsRAC harness at the shipped default: 4096 and 8192 both saturate during the post-formation cold storm (DENIED_DEDUP_FULL -> retransmit exhaustion plus downstream GCS control timeouts); 16384 is green across 3 consecutive runs on all 4 nodes (errors 0, dedup_full_count 0, ~230k qps/node). Raise the default to that floor (ceiling 65536 unchanged; ~138MB shmem per serving node at 8448B/entry) and sweep the default assertions in t/112 and t/366. Spec: spec-7.2a-gcs-block-dedup-capacity-gc.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
GCS block dedup HTAB capacity + pressure-driven eager reclaim, keeping the retransmit-dedup correctness contract fail-closed.
Local gates: full clean cluster_unit 165/165, cluster_regress 13/13, PG regression 219/219, TAP band (t/366, t/110-116, t/215) green, clang-format/cppcheck/comment-headers clean.
Spec: spec-7.2a-gcs-block-dedup-capacity-gc.md
🤖 Generated with Claude Code