Skip to content

Add selective index updates (n/m) to the heap table AM#23

Draft
gburd wants to merge 39 commits intomasterfrom
tepid
Draft

Add selective index updates (n/m) to the heap table AM#23
gburd wants to merge 39 commits intomasterfrom
tepid

Conversation

@gburd
Copy link
Copy Markdown
Owner

@gburd gburd commented Apr 6, 2026

Updates trigger n of m index updates rather than all, none, or only summarizing. Table AMs have the ability to influence this behavior by changing modified_idx_attrs.

@github-actions github-actions bot force-pushed the master branch 20 times, most recently from 70bedd9 to 6ab967f Compare April 7, 2026 11:20
  - Hourly upstream sync from postgres/postgres (24x daily)
  - AI-powered PR reviews using AWS Bedrock Claude Sonnet 4.5
  - Multi-platform CI via existing Cirrus CI configuration
  - Cost tracking and comprehensive documentation

  Features:
  - Automatic issue creation on sync conflicts
  - PostgreSQL-specific code review prompts (C, SQL, docs, build)
  - Cost limits: $15/PR, $200/month
  - Inline PR comments with security/performance labels
  - Skip draft PRs to save costs

  Documentation:
  - .github/SETUP_SUMMARY.md - Quick setup overview
  - .github/QUICKSTART.md - 15-minute setup guide
  - .github/PRE_COMMIT_CHECKLIST.md - Verification checklist
  - .github/docs/ - Detailed guides for sync, AI review, Bedrock

  See .github/README.md for complete overview

Complete Phase 3: Windows builds + fix sync for CI/CD commits

Phase 3: Windows Dependency Build System
- Implement full build workflow (OpenSSL, zlib, libxml2)
- Smart caching by version hash (80% cost reduction)
- Dependency bundling with manifest generation
- Weekly auto-refresh + manual triggers
- PowerShell download helper script
- Comprehensive usage documentation

Sync Workflow Fix:
- Allow .github/ commits (CI/CD config) on master
- Detect and reject code commits outside .github/
- Merge upstream while preserving .github/ changes
- Create issues only for actual pristine violations

Documentation:
- Complete Windows build usage guide
- Update all status docs to 100% complete
- Phase 3 completion summary

All three CI/CD phases complete (100%):
✅ Hourly upstream sync with .github/ preservation
✅ AI-powered PR reviews via Bedrock Claude 4.5
✅ Windows dependency builds with smart caching

Cost: $40-60/month total
See .github/PHASE3_COMPLETE.md for details

Fix sync to allow 'dev setup' commits on master

The sync workflow was failing because the 'dev setup v19' commit
modifies files outside .github/. Updated workflows to recognize
commits with messages starting with 'dev setup' as allowed on master.

Changes:
- Detect 'dev setup' commits by message pattern (case-insensitive)
- Allow merge if commits are .github/ OR dev setup OR both
- Update merge messages to reflect preserved changes
- Document pristine master policy with examples

This allows personal development environment commits (IDE configs,
debugging tools, shell aliases, Nix configs, etc.) on master without
violating the pristine mirror policy.

Future dev environment updates should start with 'dev setup' in the
commit message to be automatically recognized and preserved.

See .github/docs/pristine-master-policy.md for complete policy
See .github/DEV_SETUP_FIX.md for fix summary

Optimize CI/CD costs by skipping builds for pristine commits

Add cost optimization to Windows dependency builds to avoid expensive
builds when only pristine commits are pushed (dev setup commits or
.github/ configuration changes).

Changes:
- Add check-changes job to detect pristine-only pushes
- Skip Windows builds when all commits are dev setup or .github/ only
- Add comprehensive cost optimization documentation
- Update README with cost savings (~40% reduction)

Expected savings: ~$3-5/month on Windows builds, ~$40-47/month total
through combined optimizations.

Manual dispatch and scheduled builds always run regardless.
@github-actions github-actions bot force-pushed the master branch 4 times, most recently from dc10453 to e74507c Compare April 7, 2026 15:29
gburd added 5 commits April 7, 2026 12:09
This commit introduces test infrastructure for verifying Heap-Only Tuple
(HOT) update functionality in PostgreSQL. It provides a baseline for
demonstrating and validating HOT update behavior.

Regression tests:
- Basic HOT vs non-HOT update decisions
- All-or-none property for multiple indexes
- Partial indexes and predicate handling
- BRIN (summarizing) indexes allowing HOT updates
- TOAST column handling with HOT
- Unique constraints behavior
- Multi-column indexes
- Partitioned table HOT updates

Isolation tests:
- HOT chain formation and maintenance
- Concurrent HOT update scenarios
- Index scan behavior with HOT chains
Refactor executor update logic to determine which indexed columns have
actually changed during an UPDATE operation rather than leaving this up
to HeapDetermineColumnsInfo() in heap_update().

Applied patch v38-0002 with offsets (-16 lines in heapam.h, various
other files with 1-10 line offsets).

# Conflicts:
#	src/backend/access/heap/heapam.c
#	src/backend/access/heap/heapam_handler.c
#	src/include/access/heapam.h
#	src/include/access/tableam.h
Replace expression-based index_unchanged_by_update() with per-UPDATE
hint mechanism using ExecSetIndexUnchanged() based on modified_idx_attrs.

Applied patch v20260316c-0009 with offsets.
Replaces the three-value TU_UpdateIndexes enum with a cleaner
bitmapset-based approach using MODIFIED_IDX_ATTRS_ALL_IDX sentinel bit.

Major API changes:
- Removed TU_UpdateIndexes enum (TU_None, TU_All, TU_Summarizing)
- Changed simple_heap_update() signature to use Bitmapset **modified_idx_attrs
- Removed summarized_only parameter from HeapUpdateHotAllowable()
- Updated ExecSetIndexUnchanged() signature
- Replaced EIIT_ONLY_SUMMARIZING with EIIT_ALL_INDEXES

Applied patch v20260316c-0010 with manual conflict resolution in:
- heapam.c (13 hunks manually applied)
- nodeModifyTable.c (2 hunks manually applied)
- execReplication.c (1 hunk manually applied)
The function was leaking memory by not freeing strings and node trees
created when parsing index expressions and predicates.

Use a temporary memory context for string and node tree allocations,
but keep the Bitmapset (attrs) in the calling context. This ensures:
1. Strings from TextDatumGetCString() are freed
2. Node trees from stringToNode() are freed
3. The returned Bitmapset remains valid after context deletion

This fixes the unbounded memory growth in postgres that was consuming
all system RAM during index scans.
gburd added 11 commits April 8, 2026 11:16
Catalog relations must not use HOT updates because CatalogIndexInsert()
lacks the selective index insertion logic needed to maintain B-tree indexes
during HOT updates. Without new index entries for modified indexed columns,
catalog lookups by new key values would fail.

This restores the guard removed in commit 7cc0c9d.
Add NULL checks before calling bms_overlap() in heap_hot_search_buffer().
When HeapUpdateModifiedIdxAttrs() returns NULL (can't determine modified
attributes due to unbuilt relcaches), conservatively assume all indexed
attributes were modified to avoid infinite loops.

This fixes the hang but there remains a performance issue where certain
UPDATE operations take excessively long (250+ seconds for simple queries).
Further investigation needed.

Fixes the infinite loop reported in constraints test, though performance
degradation persists.
Due to severe performance degradation, HOT selective index updates are
temporarily disabled by setting use_hot_update = false unconditionally.

Performance testing shows:
- Without HOT fixes: infinite loop (timeout after 120+ seconds)
- With NULL-check fix + HOT enabled: completes in 250 seconds
- With NULL-check fix + HOT disabled: completes in 159 seconds

The performance issue appears to be deeper than just the HOT code - even
with HOT disabled, simple 5-row UPDATEs with unique constraints take 2-3
minutes instead of milliseconds.

This change allows the memory leak and build warning fixes to be committed
while the HOT performance issue is investigated separately.
Add NULL checks before calling bms_overlap() when HeapUpdateModifiedIdxAttrs()
returns NULL (which happens when relcaches aren't built yet). Previously, this
caused bms_overlap(NULL, interesting_attrs) to return false, preventing the
loop from breaking and causing an infinite loop.

The fix conservatively assumes all indexed attributes were modified when
modified_attrs is NULL, which is the safe behavior to prevent infinite loops
while maintaining correctness.

This addresses the hang observed in the constraints test when updating rows
with DEFERRABLE UNIQUE constraints.
The infinite loop bug in heap_hot_search_buffer() has been fixed with proper
NULL checks for HeapUpdateModifiedIdxAttrs() return values. Testing shows the
UPDATE query runs fast (11ms) in clean postgres environments - the 159-second
hangs observed in pg_regress test harness appear to be environment-specific
issues, not code bugs.

With the NULL checks in place, it's safe to re-enable HOT selective index
updates for non-catalog relations.
Remove the IsCatalogRelation() guards that were preventing HOT selective
index updates on catalog tables. The comments claimed CatalogIndexInsert()
"does not yet support" selective updates, but this was incorrect.

Analysis shows that CatalogIndexInsert() fully supports selective index
updates via the modified_idx_attrs parameter:
- It checks if modified_idx_attrs is NULL or contains MODIFIED_IDX_ATTRS_ALL_IDX
- For HeapOnly tuples with no modified summarizing columns, it returns early
- For onlySummarized updates, it skips non-summarizing indexes (lines 182-183)

The catalog update path (CatalogTupleUpdate) calls simple_heap_update() which
returns modified_idx_attrs, then passes it to CatalogIndexInsert() for selective
index updates. The infrastructure has been working correctly all along.

Also removed debug logging from heap_hot_search_buffer() and
HeapUpdateModifiedIdxAttrs() that was added during investigation.

Changes:
- src/backend/access/heap/heapam.c: Remove IsCatalogRelation() guards,
  update comments, remove debug logging
- src/backend/access/heap/heapam_handler.c: Enable indexed_attrs for catalogs
This avoids scanning pg_index twice (once in CatalogOpenIndexes, once in
simple_heap_update) and eliminates circular dependencies during bootstrap.

Key changes:

1. New function RelationBuildIndexAttrBitmapFromOpenIndexes() in relcache.c
   - Builds and caches indexed attribute bitmaps from already-open indexes
   - Extracts attributes using IndexGetAttrBitmap() on each open index
   - Stores results in relation's relcache (rd_indexedattr, rd_keyattr, etc.)
   - Avoids scanning pg_index, which is critical during bootstrap

2. Modified CatalogTupleUpdate() and CatalogTupleUpdateWithInfo() in indexing.c
   - After CatalogOpenIndexes() opens all indexes, build and cache their attrs
   - simple_heap_update() then uses the cached bitmaps via RelationGetIndexAttrBitmap()
   - This avoids the second pg_index scan that simple_heap_update() would normally do

3. Benefits:
   - Works during bootstrap (no circular pg_index dependency)
   - Performance improvement (one pg_index scan instead of two)
   - Cleaner separation: catalog layer handles index opening and attr caching,
     heap layer just uses the cached information

4. User's refactoring of simple_heap_update() preserved:
   - Returns Bitmapset* instead of using out parameter
   - Takes idx_attrs from RelationGetIndexAttrBitmap() early
   - Passes idx_attrs to HeapUpdateModifiedIdxAttrs() to avoid bootstrap checks there

initdb completes successfully and creates valid database cluster.
Remove test database directories, log files, and build artifacts:
- pgdata-debug-test/ (test database cluster)
- final-regression-test*.log (test logs)
- .history updates

These were temporary test artifacts from development and debugging.
Add detailed comments explaining how relcache invalidation is handled
correctly between opening indexes and caching indexed attribute bitmaps.

The code is safe because:
- If invalidation occurs during CatalogOpenIndexes (via
  AcceptInvalidationMessages called during index_open), the relation's
  rd_attrsvalid flag is cleared
- RelationBuildIndexAttrBitmapFromOpenIndexes checks rd_attrsvalid and
  rebuilds bitmaps when false
- The rebuild uses the current set of open indexes which reflect the
  post-invalidation state
- rd_attrsvalid is set atomically after all bitmap fields are populated

This addresses concerns about invalidation safety during catalog updates.
Add explanation that HOT selective index updates work with ALL index
access methods (B-tree, Hash, GiST, GIN, SP-GiST, BRIN, Bloom) without
requiring index AM-specific code.

Key points:
- HOT chain following happens in the heap AM layer, not in index AMs
- Index AMs only return TIDs; heap AM handles tuple fetching and HOT chains
- indexed_attrs from irel->rd_indattr is used uniformly for all index types
- Even lossy indexes (GiST, GIN) work correctly because they return exact TIDs
- No amcanhotselective flag needed

This addresses concerns about index AM compatibility and confirms the
implementation is correct for all index types.
Add detailed comments explaining why pruning logic correctly handles
INDEXED_UPDATED tuples (HOT selective index updates):

1. Pruning treats INDEXED_UPDATED same as HOT_UPDATED: follows chains,
   creates redirects when root is pruned
2. LP_REDIRECT items don't need to store modified attribute info
3. The correctness argument: if an index entry leads to a redirect, it
   inherently means that index's attributes weren't modified (otherwise
   the index would have a new entry pointing directly to updated tuple)
4. When following redirects, we don't set prev_offnum, so indexed attribute
   checking is skipped - this is correct behavior

This addresses concerns about pruning correctness with INDEXED_UPDATED bit.
@github-actions github-actions bot force-pushed the master branch 16 times, most recently from 7b7f25c to c2e3fc8 Compare April 10, 2026 14:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant