Skip to content

Deduplicate external leaf partitions in the metadata table list - #126

Open
talmacschen-arch wants to merge 2 commits into
apache:mainfrom
talmacschen-arch:fix/dup-external-leaf-partition
Open

talmacschen-arch wants to merge 2 commits into
apache:mainfrom
talmacschen-arch:fix/dup-external-leaf-partition

Conversation

@talmacschen-arch

Copy link
Copy Markdown

Problem

gpbackup --leaf-partition-data aborts while writing pre-data metadata as soon as the database contains an external (foreign-table) leaf partition:

[INFO]:-Writing pre-data metadata
[CRITICAL]:-Dependency resolution failed; see log file ... for details. This is a bug, please report.
github.com/apache/cloudberry-backup/backup.TopologicalSort         backup/dependencies.go:145
github.com/apache/cloudberry-backup/backup.backupDependentObjects  backup/wrappers.go:627
github.com/apache/cloudberry-backup/backup.backupPredata           backup/backup.go:285

It is not a real dependency cycle: the verbose log prints Not yet visited: with no objects under it. TopologicalSort bails out in the len(slice) != len(sorted) branch because two entries in its input share the same UniqueID, so the sorted output ends up one element shorter than the input.

The duplicate comes from SplitTablesByPartitionType. Under --leaf-partition-data, an external leaf partition is appended to metadataTables twice on Cloudberry / GPDB 7+:

  • the external-partition branch appends it — this branch exists only to add the _ext_part_ suffix for GPDB 6 and earlier;
  • the common GPDB7+/CBDB branch, which emits every table, appends it again.

Reproduces on any database with an external leaf partition, with or without --single-data-file.

Fix

Restrict the external-partition branch to GPDB 6 and earlier, where the suffix is needed and the common branch does not run. On GPDB7+/CBDB the external leaf partition is now emitted once by the common branch, exactly like a native leaf partition.

if table.IsExternal && table.PartitionLevelInfo.Level == "l" &&
    connectionPool.Version.IsGPDB() && connectionPool.Version.Before("7") {
    table.Name = AppendExtPartSuffix(table.Name)
    metadataTables = append(metadataTables, table)
}

Test

Added a unit test asserting that an external leaf partition appears in the metadata list exactly once under --leaf-partition-data on GPDB7+/CBDB. It fails (metadataTables length 3) before the fix and passes after.

Also verified end to end on a live Cloudberry 2.1.0 cluster: gpbackup --leaf-partition-data on a database with an external leaf partition fails with Dependency resolution failed without this change, and completes with Backup completed successfully with it.

Under --leaf-partition-data, SplitTablesByPartitionType appends an
external (foreign-table) leaf partition to metadataTables twice on
Cloudberry / GPDB 7+:

  - the external-partition branch appends it (this branch exists only to
    add the _ext_part_ suffix for GPDB 6 and earlier), and
  - the common GPDB7+/CBDB branch, which emits every table, appends it
    again.

Both copies carry the same UniqueID, so TopologicalSort later produces a
sorted slice shorter than its input and aborts predata with:

  [CRITICAL]:-Dependency resolution failed; ... This is a bug, please report.

(stack: TopologicalSort <- backupDependentObjects <- backupPredata). The
"Not yet visited" list in the verbose log is empty, which rules out a
real dependency cycle and points at a dropped, de-duplicated object.

It reproduces on any database that has an external leaf partition when
backing up with --leaf-partition-data, with or without --single-data-file.

Restrict the external-partition branch to GPDB 6 and earlier, where the
suffix is needed and the common branch does not run. On GPDB7+/CBDB the
external leaf partition is now emitted once by the common branch, exactly
like a native leaf partition.

Add a unit test asserting that an external leaf partition appears in the
metadata list exactly once under --leaf-partition-data on GPDB7+/CBDB; it
fails (length 3) before this change and passes after.
@talmacschen-arch

Copy link
Copy Markdown
Author

@liang8283 could you please help review this PR when you have a chance? Thanks!

@liang8283

liang8283 commented Oct 30, 2026 •

Copy link
Copy Markdown

Approved.

Tested this PR end to end on a live Cloudberry cluster. I built gpbackup/gprestore from this PR and from its base commit (8b045de5), and compared the two.

Test schema: a range-partitioned table sales with two regular leaf partitions, plus a readable external table sales_ext attached as a third leaf partition (ALTER TABLE sales ATTACH PARTITION sales_ext FOR VALUES ...). The database also has one plain table.

Results

Scenario Base (8b045de5) This PR
gpbackup --leaf-partition-data ❌ Dependency resolution failed (empty Not yet visited: list) ✅ Backup completed
gpbackup --include-table public.sales (no --leaf-partition-data) ❌ Dependency resolution failed ✅ Backup completed
Default full backup — ✅ Backup completed
gprestore of the two PR backups above — ✅ Restore completed; partition hierarchy and external leaf attachment intact, row counts match the source

With the PR, the metadata file contains CREATE READABLE EXTERNAL TABLE public.sales_ext and its ATTACH PARTITION statement exactly once.

Notes

  • The --include-table case shows the bug was not specific to --leaf-partition-data. Any backup that goes through the LEAF_PARTITION_DATA || len(includeList) > 0 branch was affected, and this PR fixes that path as well. It would be worth mentioning this in the description and adding a unit test with a non-empty includeList and LEAF_PARTITION_DATA=false.
  • The new unit test sets the version to GPDB 7.0.0, so it never exercises the IsCBDB() arm, which is where this bug was reported. Running the same case against a CBDB version as well would guard against regressions.
  • Not covered by my testing: --single-data-file and GPDB 6.

Address review feedback on the unit test for the external leaf partition
deduplication fix:

  - Add a Cloudberry (IsCBDB) variant. The bug was reported on Cloudberry,
    which takes the same GPDB7+ code path, but the original test only set a
    GPDB 7.0.0 version and never exercised the IsCBDB arm.
  - Add a variant with a non-empty includeList and --leaf-partition-data off.
    The duplication happens in the LEAF_PARTITION_DATA || len(includeList) > 0
    branch, so --include-table alone (without --leaf-partition-data) triggered
    it too; this guards that path.

All three variants (GPDB7+ with --leaf-partition-data, Cloudberry with
--leaf-partition-data, GPDB7+ with a non-empty includeList) fail with length 3
before the fix and pass after it.
@talmacschen-arch

Copy link
Copy Markdown
Author

Thanks for the detailed verification and the two suggestions — both addressed:

  • Cloudberry (IsCBDB) variant added. The original test only set a GPDB 7.0.0 version; there's now a variant that sets a CBDB version so the IsCBDB() arm (where the bug was reported) is exercised directly.
  • Non-empty includeList + --leaf-partition-data off variant added, since the duplication happens in the LEAF_PARTITION_DATA || len(includeList) > 0 branch and --include-table alone triggered it too.

All three variants (GPDB7+ with --leaf-partition-data, Cloudberry with --leaf-partition-data, GPDB7+ with a non-empty includeList) fail with length 3 before the fix and pass after — I verified this by temporarily reverting the production change and watching all three fail.

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.

2 participants