Skip to content

Build Not yet confirmed elsewhere1 publisher 2 min read Published

PostgreSQL 19 beta's REPACK CONCURRENTLY kept a live client's worst stall to 0.33 seconds

REPACK CONCURRENTLY stalled a live client 0.33 seconds at worst in PostgreSQL 19 beta 4, against 3-6 seconds under VACUUM FULL, a dev.to post reported. The runs pit it against VACUUM FULL alone, so retiring pg_repack is still an open question.

The Engineer · Build desk

How we use AI Send a correction

Illustration accompanying PostgreSQL 19 beta's REPACK CONCURRENTLY kept a live client's worst stall to 0.33 seconds
Generated illustration
Worst stall barely moved with ten clients REPACK CONCURRENTLY's worst-case stall per client with ten clients and with one, and the statement's wall-clock time under either command.

Key figures: with ten clients, REPACK CONCURRENTLY's worst-case stall per client was 0.28 seconds, against 0.33 seconds with one client. The statement took 3 to 6 seconds of wall-clock time under either VACUUM FULL or REPACK CONCURRENTLY, depending on the run.

Worst stall barely moved with ten clients
FigureValueNoteClaim
REPACK CONCURRENTLY worst stall, ten clients0.28 seconds7
Worst stall, one client0.33 seconds7
Statement time, either command, at least3 secondsup to 6 seconds, depending on the run4

What happened

  • After half the rows of a 2,000,000-row table were deleted, a plain VACUUM left it at 531 MB, while VACUUM FULL and REPACK each brought it down to 266 MB.
  • REPACK CONCURRENTLY copies the table in the background, uses logical decoding to capture changes made during the copy, and takes its lock only at the end for the file swap.
  • The author drove a background client through one UPDATE and one SELECT on a live row, counted calls over 250ms as blocked, and ran each command three times.
  • REPACK CONCURRENTLY wrote more WAL than VACUUM FULL in every measurement, by roughly 4% to 25%.

Why it matters

  • exposure Ten clients reaching the swap window together take ten short stalls at once, and the author says that sum belongs in a capacity plan ahead of the single-client worst case.
  • cost Teams limited by WAL-shipping bandwidth to a replica pay for the shorter lock in extra bytes on that link, according to the author.
  • decision Anyone repacking several tables at once has to size max_repack_replication_slots first, since each concurrent run needs its own slot and the default is 5.
  • contradiction The documentation flags an extra temp file during the copy, yet the test saw a 3 MB difference at a light write rate, so it settles nothing for write-heavy tables.

VACUUM FULL takes an ACCESS EXCLUSIVE lock up front and holds it until the swap is done, so the client in the test was blocked for essentially the whole statement [5]. The statement took 3-6 seconds under either command [4]. The post's headline puts the old lock at five seconds, inside that range [16]. Taking the statement time as the VACUUM FULL block, the worst REPACK stall of 0.33 seconds [7] is roughly 9 to 18 times shorter [18].

We'd expect the lock behavior to carry over to other tables, because it follows from where the lock is taken [5][6]. The 0.33 seconds is a different matter. It was measured on one table of 531 MB [2], with a client looping on a live row [3] and a write load of a few thousand single-row updates a second [12]. It transfers to tables and workloads that look like that one.

With ten clients on the table, the per-client worst case barely moved: 0.28s against 0.33s with one client [7]. Both figures sit above the 250ms line the author used to count a call as blocked [3].

The extra WAL comes from the logical-decoding worker. It decodes and replays every change that landed on the old copy during the rewrite, and that replay goes through WAL like any other write [10]. The width of the range depends on how much VACUUM FULL varied between runs [9].

Disk was nearly a tie. Peak data-directory growth was 276 MB for REPACK CONCURRENTLY and 279 MB for VACUUM FULL, a 3 MB gap of about 1% [11][19]. The author expects a heavier write rate to look different and wrote that there was no time to push the load that far [12].

Each concurrent repack runs its own logical decoding session, so CONCURRENTLY needs a free slot, gated by max_repack_replication_slots [13]. The default is 5 [13]. With two tables under default settings, the author could not trigger the limit [14].

The post describes the option as "supposed to do the rewrite that used to need VACUUM FULL or the third-party pg_repack extension" [15]. The timed runs it reports are VACUUM FULL against REPACK, and pg_repack does not appear in them [3].

What to watch

  • A head-to-head of REPACK CONCURRENTLY against pg_repack on the same bloat, with the same client loop and WAL counters.
  • The same test at a write rate well above a few thousand single-row updates a second, to see whether the temp file the documentation flags grows past noise.
  • Whether REPACK behavior and the max_repack_replication_slots default of 5 survive unchanged from beta 4 to the final PostgreSQL 19 release.

Clarity's read

What the record supports and how the coverage leans. The claims behind it follow.

Reality

Evidence50
Adoption
Insufficient
Hype gap+10
Incentives
Insufficient
Confidence55
Why these scores

Claim ledger

Ranked by verification strength, evidence, and original report placement.

  1. [1]

    The author built a table of 2,000,000 rows of 200-byte text and deleted every even-numbered row, leaving 1,000,000 rows.

    Reported Supported Source: dev.to post authorView cited source
  2. [2]

    The table measured 531 MB before and after deleting 1,000,000 of 2,000,000 rows, even after a plain VACUUM; after either VACUUM FULL or REPACK it measured 266 MB.

    Reported Supported Source: dev.to post authorView cited source
  3. [3]

    On PostgreSQL 19 beta 4, a background client ran one UPDATE and one SELECT against a live row in a tight loop, with a 250ms threshold for counting a call as blocked; VACUUM FULL and REPACK (CONCURRENTLY) were each run three times against freshly rebuilt bloat.

    Reported Supported Source: dev.to post authorView cited source

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.

Add to ChatGPTAdd to Claude

Sources

1 independent publisher whose own reporting we read for this story.

  1. dev.to

    1 article · October 14, 2026

    PostgreSQL 19's REPACK CONCURRENTLY swaps a five-second table lock for one under a second

Share your take

Let Clarity write the post for you.

Signed-in readers get a short post drafted on this story in the register they choose — narrative, analytical, or a direct position — editable to the last word before it goes anywhere. The share buttons at the top of this story work without an account.

Topics and entities

Follow any of these and your For You feed starts watching them — no settings page required.

Topics

Loading related stories