Postgres 19's REPACK CONCURRENTLY trades VACUUM FULL's table lock for a 3.8x rise in average insert latency
Postgres 19's REPACK CONCURRENTLY cut a 200M-row table from 19 GB to 4117 MB in about 66 seconds in depesz's test, with inserts running. Average insert latency rose from 1.469 ms to 5.536 ms during that window.
The Engineer · Build desk

During the repack, the average insert took 5.536 ms, against 1.469 ms before it. The slowest insert during the repack took 63 ms. The repack took about 66 seconds.
| Figure | Value | Note | Claim |
|---|---|---|---|
| Average insert latency during the repack | 5.536 ms | 3 | |
| Average insert, before | 1.469 ms | 3 | |
| Slowest insert in repack | 63 ms | 4 | |
| Repack run, about | 66 seconds | 2 |
What happened
- VACUUM FULL and CLUSTER hold an ACCESS EXCLUSIVE lock for the whole rewrite, so every read and write on the table waits until it finishes.
- CONCURRENTLY builds the new copy under a SHARE UPDATE EXCLUSIVE lock and decodes concurrent changes through a replication slot and a background worker, then replays them just before the file swap.
- The commit that added the option went in on April 6, 2026, committed by Alvaro Herrera and mostly Antonin Houska's code.
- PostgreSQL 19 is still in beta, and the official postgres image publishes a 19beta4 tag to try it on.
Why it matters
- What it costs cost Writers pay for the online rewrite: about 4 ms more per insert on average for the roughly 66 seconds of the run, where VACUUM FULL charges in blocked sessions.
- What it limits constraint A table needs a primary key or an index-based replica identity before REPACK CONCURRENTLY will touch it, so keyless tables still need another route.
- Who has to decide decision Replacing pg_repack or pg_squeeze with the core command is a call to make after a rerun on a copy of your own table, since the one published run is a development build.
The two latency numbers measure different things, so take them apart. The average insert took about 3.8 times as long during the repack [14]. The slowest insert, at 63 ms [4], was about 11 times that raised average [15]. The post reports only a mean and a maximum, with nothing on the middle of the distribution and no figure for the loop's insert rate.
Writers are not blocked. Their changes are decoded through a replication slot by a background worker and replayed just before the files are swapped [6]. That is where the extra time can come from: every row the insert loop adds during the copy is a row that worker has to decode and replay.
Set that against the lock. In the post's demo, a long-running report that touched the table and has not committed [13] sits ahead of the VACUUM FULL request. The post says one ACCESS EXCLUSIVE request at the front of the line makes everything behind it wait [12]. In our view, a few milliseconds on each insert is a fair price against that queue, for a table that has to stay writable and a write path with headroom.
For the number to transfer, your row width, insert rate and hardware would all have to resemble depesz's, and the post describes neither his hardware nor his row width. The run used the PostgreSQL 19 development branch in April [1], not a release.
Extension users will recognise the design. According to CYBERTEC, pg_repack creates a set of triggers on the table, and its own pg_squeeze uses logical decoding instead [10]. The core command follows the second approach. The reported figures cover REPACK CONCURRENTLY alone, so whether it is cheaper for writers than either extension is untested here.
The release notes explain the rename: "The two former commands did similar things, but with confusing names, so unify them as REPACK. The old commands have been retained for compatibility." [11]
What to watch
- A rerun on 19beta4 or later that publishes the insert rate and latency percentiles, not only a mean and a maximum.
- Documentation or release notes stating what lock the final file swap takes and how long writers wait there.
- A same-table comparison of REPACK CONCURRENTLY against pg_repack and pg_squeeze on write latency.
Clarity's read
What the record supports and how the coverage leans. The claims behind it follow.
Reality
- Evidence55
- Adoption
- Not scored
- Hype gap+10
- Incentives
- Not scored
- Confidence50
Claim ledger
Ranked by verification strength, evidence, and original report placement.
- [1]
depesz ran the test against the PostgreSQL 19 development branch in April: 200 million rows, 80% deleted, then repack (verbose, analyze, concurrently).
- [2]
The table went from 19 GB to 4117 MB in about 66 seconds, with an insert loop running the whole time.
- [3]
Average insert latency rose from 1.469 ms to 5.536 ms during the repack.
- [4]
The slowest insert during the repack took 63 ms.
- [5]
VACUUM FULL (and its sibling CLUSTER) takes an ACCESS EXCLUSIVE lock and holds it for the entire rewrite, so every read and write on the table waits until it is done.
- [6]
The CONCURRENTLY option builds the new copy under a SHARE UPDATE EXCLUSIVE lock and decodes the changes other sessions make in the meantime through a replication slot and a background worker; those changes are replayed just before the files are swapped.
- [7]
The commit that added CONCURRENTLY went in on April 6, 2026, committed by Alvaro Herrera and mostly Antonin Houska's code.
- [8]
REPACK CONCURRENTLY refuses tables that have no primary key or index-based replica identity.
- [9]
PostgreSQL 19 is still in beta, and the official postgres Docker image publishes a 19beta4 tag.
- [10]
As CYBERTEC describes it, pg_repack works by creating a set of triggers on the table, while CYBERTEC's own extension pg_squeeze uses logical decoding instead of triggers to capture concurrent changes; that design is what landed in core.
- [11]
The PostgreSQL 19 release notes say: "The two former commands did similar things, but with confusing names, so unify them as REPACK. The old commands have been retained for compatibility."
- [12]
The damage comes from the queue that forms behind the lock: one ACCESS EXCLUSIVE request at the front of the line, and everything behind it waits.
- [13]
In the post's demo, one session plays a long-running report that touched the table and has not committed.
- [14]
Average insert latency rose by a factor of about 3.8 (5.536 / 1.469 = 3.77).
- [15]
The 63 ms slowest insert was about 11 times the 5.536 ms average during the repack (63 / 5.536 = 11.4).
- [16]
The average insert took about 4 ms longer during the repack (5.536 - 1.469 = 4.067 ms).
Get this ledger inside ChatGPT. Ask about any of our stories and the answer brings its figure, each claim’s verdict and the sources. Free, no account.
Sources
1 independent publisher whose own reporting we read for this story.
- dev.toVACUUM FULL Locks Your Table the Whole Time. Postgres 19's REPACK CONCURRENTLY Doesn't
1 article · October 15, 2026
Topics and entities
Follow any of these and your For You feed starts watching them — no settings page required.
Topics
- Database MaintenanceFollow
- Database lockingFollow
- PostgreSQLFollow