Build1 publisherNot yet confirmed elsewhere2 min readPublished
PostgreSQL 19's REPACK CONCURRENTLY rewrites bloated tables while writes continue
PostgreSQL 19 adds REPACK CONCURRENTLY, a built-in online table rewrite that a dev.to demo ran on a 30-million-row table while updates ran. Teams get an in-core alternative to VACUUM FULL and the pg_repack tool, with the risk moved to a brief lock at the end of the run.
The Engineer · Build desk

What happened
- REPACK builds a new compact copy of the table, tracks changes made to the original while copying, applies them, then runs a short final synchronization and swaps the tables.
- PostgreSQL 19 also lets REPACK reorder a table by an index, giving physical row ordering comparable to the existing CLUSTER command.
- According to the walkthrough, REPACK needs the target table to have a primary key or a suitable NOT NULL unique index.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- cost Recovering the demo heap's 1,946 MB of free space first takes disk for a copy holding at least 2,604 MB of live rows, plus rebuilt indexes.
- constraint Any migration that alters a table under REPACK has to wait for the run to finish, so repack jobs and schema deploys need separate windows.
- exposure The final swap depends on whatever transaction is oldest when it arrives, so a forgotten session or a long report can delay the step meant to be short.
- decision Teams that schedule pg_repack today can test an in-core command on PostgreSQL 19, and after-size and swap duration on their own tables will settle which one stays.
The demo table is a clean case for a rewrite. Free space was 2,040,987,872 bytes of a 5,056,790,528-byte heap, about 40 percent [12][15]. Live tuple data filled about 54 percent [16]. Dead tuples were zero [12]. The bloat is empty space, and VACUUM FULL is what PostgreSQL DBAs have relied on for years to rewrite a table and reclaim it [2].
Putting an online version of that rewrite in core is good engineering [4]. Oracle DBAs will know the pattern. The walkthrough compares REPACK to DBMS_REDEFINITION, where an interim table is populated while a materialized view log captures changes to the original, and FINISH_REDEF_TABLE switches the objects at the end [6].
I think the key requirement [7] is the price of the copy-then-replay design [5]. Applying a captured update to the new copy needs a reliable way to find the same row there, and a primary key or NOT NULL unique index provides it. A table with neither needs one added before its first run. Adding it is a schema change of its own.
The concurrent test shows DML proceeding during the copy. A second session updated batches of 10,000 and 10,001 rows while session 1 was executing REPACK (CONCURRENTLY) [13]. That is light load for a 30-million-row table [11]. For the result to transfer to a busy production table, the replay has to keep up with the write rate for the length of the copy. The final lock also has to arrive when no long-running transaction is open [9].
The available text of the walkthrough ends while session 2 is still issuing updates, before any after-size, elapsed time or lock duration is reported [14]. Until those numbers exist, I'd budget free disk equal to the table's current total relation size, 5,895 MB including 1,071 MB of indexes [11]. The heap has 1,946 MB of free space to give back [12].
What to watch
- Published after-size and final-lock duration for the 30-million-row demo table, showing how close the rewrite gets to its 2.6 GB of live data.
- How long the final lock waits under sustained write load heavier than the demo's 10,000-row update batches.
- Whether teams running pg_repack report moving scheduled bloat jobs to REPACK once they are on PostgreSQL 19.
Clarity's read
What the record supports and how the coverage leans. The claims behind it follow.
Reality
- Evidence45
- Adoption
- Insufficient
- Hype gap+15
- Incentives
- Insufficient
- Confidence50
Claim ledger
Ranked by verification strength, evidence, and original report placement.
- [1]
PostgreSQL 19 extends REPACK with support for reorganizing a table according to an index, providing functionality comparable to the physical ordering achieved with CLUSTER.
ReportedSupportedSource: dev.to walkthrough on PostgreSQL 19 REPACK2 sources— create a free account to open themView cited source - [2]
For years, PostgreSQL DBAs have relied on VACUUM FULL to rewrite tables and reclaim unused space.
- [3]
When minimizing the impact of table reorganization is important, tools such as pg_repack provide an online-style alternative to VACUUM FULL.
- [4]
PostgreSQL 19 introduces the REPACK command as a built-in solution for table reorganization, including REPACK CONCURRENTLY, which allows the table to remain available for normal database activity during most of the operation.
- [5]
REPACK creates a new, compact copy of the table, copies the existing rows into it, tracks changes made to the original table while the operation is running, applies those changes to the new table, and at the end performs a short final synchronization and swaps the old table with the reorganized one.
- [6]
The approach is conceptually similar to Oracle's DBMS_REDEFINITION, where an interim table is populated while the original remains available, a materialized view log captures changes, and DBMS_REDEFINITION.FINISH_REDEF_TABLE switches the objects.
- [7]
For REPACK, the table must have a primary key or a suitable NOT NULL unique index.
- [8]
DDL operations on the target table cannot be performed while REPACK is running.
- [9]
Long-running transactions can delay the final lock required to complete the reorganization.
- [10]
REPACK requires additional disk space because a new copy of the table and its indexes is created.
- [11]
The test table jadval1 held 30 million rows; table size 4824 MB, index size 1071 MB, total relation size 5895 MB.
- [12]
pgstattuple on jadval1 reported table_len 5,056,790,528 bytes (4823 MB), tuple_len 2,730,000,000 bytes (2604 MB of live tuple data, about 2.6 GB), 0 dead tuples, and free_space 2,040,987,872 bytes (1946 MB, about 1.9 GB).
- [13]
While REPACK (CONCURRENTLY) was executing in session 1, session 2 ran UPDATE statements on jadval1 that updated 10,000 and 10,001 rows.
- [14]
The available text of the walkthrough ends while session 2 is still issuing updates, before any post-REPACK size, elapsed time or final lock duration is reported.
- [15]
Free space was about 40 percent of the demo table's heap.
- [16]
Live tuple data was about 54 percent of the demo table's heap.
- [17]
The new copy must hold at least the 2,604 MB of live tuple data, 658 MB more than the 1,946 MB of heap free space the rewrite recovers, before counting rebuilt indexes.
Sources
1 independent publisher whose own reporting we read for this story.
- dev.toPostgreSQL 19: Online Table Reorganization with REPACK (Similar to Oracle DBMS_REDEFINITION)
1 article · October 7, 2026
Topics and entities
Follow any of these and your For You feed starts watching them — no settings page required.
Entities
- PostgreSQLFollow
- pg_repackFollow
- pgstattupleFollow
- DBMS_REDEFINITIONFollow