Skip to content

Create partitions with LIKE + ATTACH PARTITION instead of PARTITION OF - #34

Open
codybswaney wants to merge 3 commits into
masterfrom
claude/infra-6545-attach-partition
Open

codybswaney wants to merge 3 commits into
masterfrom
claude/infra-6545-attach-partition

Conversation

@codybswaney

Copy link
Copy Markdown

Problem

add_partitions/maintain create partitions with CREATE TABLE … PARTITION OF, which takes ACCESS EXCLUSIVE on the parent: any open transaction on the parent blocks it, and every later query queues behind it. Today's scheduled maintain on our production core database hit exactly this: a long-running application transaction held row locks on several parents, every partition creation timed out on the 5s lock_timeout,, with ~100 application queries piling up behind each attempt.

Solution

Create each partition as a standalone table and attach it:

  1. CREATE TABLE child (LIKE parent INCLUDING DEFAULTS CONSTRAINTS STORAGE GENERATED [COMPRESSION]) [TABLESPACE …]
  2. Add the classic-model per-partition primary key, then grants.
  3. ALTER TABLE parent ATTACH PARTITION child FOR VALUES …

ATTACH takes SHARE UPDATE EXCLUSIVE on the parent, which doesn't conflict with DML. ATTACH clones indexes, the parent-owned PK, FKs and row triggers, so INDEXES and IDENTITY (per-partition sequences) are left out of LIKE. The child is empty, so validation is free. It's still one transaction under lock_timeout and the UTC pin. Without --tablespace, the parent's tablespace carries over (as with PARTITION OF). An existing but unattached table with a new partition's name is now an error, not a silent skip that left a coverage hole.

What we validated

I ran the full suite on postgres:13.20 and 18.4: 410 pass, plus 2 tablespace tests run against a real tablespace on 13. The new src/attach-partition.test.ts uses committed tables and multiple connections to cover:

  • success behind uncommitted writes and open cursors
  • an old-path regression guard
  • timeouts behind ANALYZE / SHARE UPDATE EXCLUSIVE that leave nothing behind
  • the pg_locks mode while ATTACH waits, with concurrent INSERTs completing
  • cancellation mid-transaction
  • DEFAULT partitions
  • a full catalog diff against PARTITION OF (attributes, constraints incl. inbound/outbound FKs, indexes, triggers, ACLs incl. PUBLIC, tablespace), which is identical
  • e2e prep → fill → swap → maintain → unswap

What reviewers need to know

This doesn't remove every lock. Cloning an outbound FK still takes SHARE ROW EXCLUSIVE on the referenced table, and that's true of PARTITION OF too. An open transaction that wrote the referenced table still blocks creation, and writes to that table queue for up to lock_timeout. Reads and writes on the partitioned table itself keep flowing. When the blocker also writes the FK target, runs will still time out, just without stalling the partitioned table. A DEFAULT partition still gets ACCESS EXCLUSIVE and a scan, same as before. The README now documents both.

Rollout and validation

Merging doesn't change production. Bumping the image pin in the Terrace pgslice app (pgslice:<sha>) is what promotes this. After that, re-run maintain-core manually and confirm every table extends and no AccessExclusiveLock waits show up on the parents.

🤖 Generated with Claude Code

codybswaney and others added 3 commits October 1, 2026 09:50
…ION OF

CREATE TABLE ... PARTITION OF takes ACCESS EXCLUSIVE on the partitioned
parent, so add_partitions/maintain waits behind any open transaction that
touched the parent and every query on the parent queues behind it until
lock_timeout fires. Create each partition as a standalone table
(LIKE parent INCLUDING DEFAULTS CONSTRAINTS STORAGE GENERATED [COMPRESSION]),
add the classic per-partition primary key and grants, then ATTACH it, which
takes SHARE UPDATE EXCLUSIVE on the parent and does not conflict with DML.
ATTACH clones indexes, the parent-owned primary key, foreign keys and row
triggers, so the result is catalog-identical to PARTITION OF.

A table that already exists under a new partition's name but is not attached
now raises an error instead of being skipped silently.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PARTITION OF places a partition in the parent's tablespace when none is
given; the standalone CREATE TABLE would fall back to the database default.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@linear-code

linear-code Bot commented Oct 1, 2026

Copy link
Copy Markdown

INFRA-6545

@greptile-apps

greptile-apps Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 4/5

[Critical risk] Changes how partitions are created in the database.

The PR appears safe to merge; adding identity-column parity coverage would strengthen the new tests.

Findings

  1. P2 Identity columns lack parity coverage ▶
Fix with agent prompt
### Issue 1
src/attach-partition.test.ts:672-681
The new creation path deliberately omits `INCLUDING IDENTITY`, but neither catalog-parity fixture has an identity column. A regression when attaching a partition to an identity-column parent would therefore go unnoticed. Please add an identity-column parent to the parity tests.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

The PR replaces CREATE TABLE … PARTITION OF with standalone CREATE TABLE … LIKE followed by ATTACH PARTITION, while preserving grants, key handling, tablespace selection, and transactional rollback.

  • It adds lock-behavior, catalog-parity, and lifecycle tests, plus operational locking documentation.
  • Identity-column parents are not included in the new parity tests.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Determine missing ranges] --> B[CREATE TABLE child LIKE parent]
  B --> C[Add classic-model key and grants]
  C --> D[ATTACH PARTITION to parent]
  D --> E{More ranges?}
  E -- Yes --> B
  E -- No --> F[Commit transaction]
  B -. Error .-> G[Roll back all changes]
  D -. Error or lock timeout .-> G
Loading

Reviews (1) · Last reviewed commit: "fix: Keep new partitions in the parent's..."

Comment on lines +672 to +681
CREATE TABLE t.posts (
id varchar NOT NULL,
author_id int NOT NULL REFERENCES t.authors (id) ON DELETE CASCADE,
status text NOT NULL DEFAULT 'new' CHECK (status <> ''),
score int CHECK (score >= 0),
double_score int GENERATED ALWAYS AS (score * 2) STORED,
body text,
created_at timestamp NOT NULL DEFAULT now(),
PRIMARY KEY (id, created_at)
) PARTITION BY RANGE (created_at)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Identity columns lack parity coverage The new creation path deliberately omits INCLUDING IDENTITY, but neither catalog-parity fixture has an identity column. A regression when attaching a partition to an identity-column parent would therefore go unnoticed. Please add an identity-column parent to the parity tests.

Knowledge Base Used: Partitioned table model

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/attach-partition.test.ts
Line: 672-681

Comment:
**Identity columns lack parity coverage** The new creation path deliberately omits `INCLUDING IDENTITY`, but neither catalog-parity fixture has an identity column. A regression when attaching a partition to an identity-column parent would therefore go unnoticed. Please add an identity-column parent to the parity tests.

**Knowledge Base Used:** [Partitioned table model](https://app.greptile.com/workos/-/custom-context/knowledge-base/workos/pgslice/-/docs/partitioned-table-model.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant