Skip to content

Add citus_internal.distribute_object repair UDF#8621

Open
onurctirtir wants to merge 16 commits into
citusdata:mainfrom
onurctirtir:onurctirtir-cuddly-waffle
Open

Add citus_internal.distribute_object repair UDF#8621
onurctirtir wants to merge 16 commits into
citusdata:mainfrom
onurctirtir:onurctirtir-cuddly-waffle

Conversation

@onurctirtir

@onurctirtir onurctirtir commented Jun 26, 2026

Copy link
Copy Markdown
Member

DESCRIPTION: Adds citus_internal.distribute_object() tool that can be used to manually propagate an object to workers.

This is a super-user only UDF that can be used by admins to
re-create an object as distributed in cluster-wide.

Adds a superuser-only UDF citus_internal.distribute_object(classid, objid)
that recreates a single object on all worker nodes and records it in
pg_dist_object on the coordinator and all workers, in an idempotent manner.

This is a repair tool for objects that should have been distributed but
were not (e.g. due to a bug). It reuses GetDependencyCreateDDLCommands to
build the DDL for the object itself and deliberately ignores the object's
dependencies; the caller is responsible for those.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@codecov

codecov Bot commented Jun 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 83.54430% with 13 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.73%. Comparing base (efa65fc) to head (295ed81).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #8621      +/-   ##
==========================================
- Coverage   88.73%   88.73%   -0.01%     
==========================================
  Files         288      288              
  Lines       64384    64464      +80     
  Branches     8108     8114       +6     
==========================================
+ Hits        57133    57202      +69     
- Misses       4909     4920      +11     
  Partials     2342     2342              
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

onurctirtir and others added 3 commits June 26, 2026 13:30
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Instead of broadcasting create-or-replace style DDL to every worker, probe
each node for the object by name via pg_get_object_address() and only open a
connection and run the create DDL on the nodes that are missing it.

This avoids worker_create_or_replace_object()'s rename-on-conflict behavior
on nodes that already have the object. We accept the small TOCTOU window
between the existence check and the create: the worst case is a loud failure,
which is fine for a superuser-only repair UDF.

Also align the local-existence error message with the regression test's
expected output ("does not exist on the local node").

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@onurctirtir onurctirtir force-pushed the onurctirtir-cuddly-waffle branch from fce694f to 55cbf18 Compare June 26, 2026 14:45
Onur Tirtir and others added 11 commits June 29, 2026 11:09
Adds a force_recreate boolean (default false) to citus_internal.distribute_object. When false (the default) we keep skip-missing-only: a node is skipped if it already has the object, avoiding worker_create_or_replace_object's rename-on-conflict. When true, the per-node existence probe is skipped and the create DDL is re-applied on every node, which syncs drift such as role grants/options that were never propagated to nodes that already have the role.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Cover function, schema, collation, type, ts dict/config, sequence, view, publication, role, extension and foreign server with: no-force create, force on existing, and force-resync after coordinator-only drift.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ction

Previously the per-node DDL was committed in its own session via SendCommandListToWorkerOutsideTransactionWithConnection so that the created object would be visible to the separate metadata connection used by MarkObjectDistributedViaSuperUser. That left object creation and the pg_dist_object records non-atomic.

Send the DDL over the same REQUIRE_METADATA_CONNECTION that MarkObjectDistributedViaSuperUser reuses, inside a coordinated 2PC transaction, so creation and metadata records commit or roll back together. The existence probe now runs inside a savepoint so a name-resolution error does not abort the surrounding remote transaction.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The per-node existence probe called pg_get_object_address() directly, which raises when the object is absent. To keep that error from aborting our coordinated 2PC remote transaction, the probe was wrapped in a SAVEPOINT/ROLLBACK TO SAVEPOINT dance in C.

Add a small citus_internal.object_exists(text, text[], text[]) PL/pgSQL helper that traps the lookup error and returns a boolean. The probe now runs as a plain SELECT that never errors, so RemoteObjectExists() drops all savepoint handling and just reads the boolean result.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Recreating the object over a coordinated 2PC metadata connection required probing existence inside the same remote transaction, which in turn needed either a SAVEPOINT/ROLLBACK dance or a PL/pgSQL EXCEPTION helper to keep pg_get_object_address()'s not-found error from aborting the transaction. There is no fully generic non-erroring existence check in core SQL (the to_reg* family does not cover text search, publication, extension or foreign server objects), so both options were undesirable.

Go back to the original approach: send the create DDL over a separate FORCE_NEW_CONNECTION outside any coordinated transaction. The existence probe's not-found error is then naturally contained on that standalone connection, so no savepoint or exception handling is needed. The trade-off is that recreation is no longer atomic across nodes, which is acceptable for a re-runnable superuser repair UDF.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ommandReturnsRow

Cover scenarios iv (object present on coordinator + one worker, repaired on the missing workers) and v (object already present everywhere, not recreated) for every supported object type.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
For the role (the motivating case) and the function (representative type), assert that the object is absent from pg_dist_object on the coordinator and every worker before distribute_object(force_recreate := false) and present on all of them afterwards. The pg_dist_object probe joins the catalog by name instead of casting to a reg* type so it does not error on workers where the object does not exist yet.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
After a force_recreate := true call on an object that already exists everywhere, assert that pg_dist_object still holds exactly one row on the coordinator and every worker for the function and the role, proving the distributed mark is not duplicated. This also adds an explicit ii-a block to the role section.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…hots

- dependencies.c: add the second blank line citus_indent requires between top-level definitions (check-style failure).

- multi_extension.out and upgrade_list_citus_objects.out: update the citus_internal.distribute_object signature to (oid,oid,boolean) to reflect the force_recreate parameter (multi_extension, check-multi-mx and check-pg-upgrade failures).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
RemoteCommandOnNodeReturnsRow re-raised statement-level errors (e.g. the
existence probe's "function ... does not exist" on a worker missing the
object), which broke the missing-object code path and failed
check-multi-1-create-citus. Restore the original 3-way handling:
connection-level failure raises, while a statement-level error is treated
as "no rows returned" so the probe reports the object as absent and the
caller creates it. Keeps the OUTSIDE_TRANSACTION self-owned connection.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@onurctirtir onurctirtir marked this pull request as ready for review June 30, 2026 14:58
Comment thread src/backend/distributed/commands/dependencies.c Outdated
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