Summary
Several PostgreSQL backends performing concurrent UPDATEs became stuck at 100% CPU while inserting into a RUM tsvector index. The backends did not respond to pg_terminate_backend(): it returned true, and debugger inspection showed both InterruptPending = 1 and ProcDiePending = 1, but the processes continued spinning.
Rebuilding the affected RUM indexes resolved the problem. The same write and indexed-search paths now complete in milliseconds under the original workload.
Environment
- PostgreSQL 17.7
- RUM package 1.3.15; extension version reports
1.3
- Linux on ARM64
- RUM operator class:
rum_tsvector_ops
Generic schema/workload description
The affected table contains a tsvector maintained by a BEFORE INSERT OR UPDATE trigger. The trigger combines several ordinary text fields using the simple text-search configuration.
There were two RUM indexes on the same tsvector:
- A full-table RUM index.
- A partial RUM index restricted by two ordinary scalar-column predicates.
The workload performs concurrent updates to rows in this table. Even metadata-only updates cause the trigger to recompute the tsvector.
Observed behavior
- Multiple concurrent
UPDATE backends consumed approximately one CPU core each indefinitely.
- Additional updates became blocked on the spinning transactions.
pg_stat_activity showed the spinning backends as active, with no wait event.
pg_terminate_backend(pid) returned true, but the backends did not exit.
- Debugger inspection confirmed the termination signal was pending.
- A concurrent REINDEX could not start its build because it was waiting for the spinning writer transactions.
The relevant portion of a representative stack was:
ResourceOwnerEnlarge
StartReadBuffer
ReadBufferExtended
<frames in rum.so>
ExecInsertIndexTuples
This indicates the loop was in the RUM insertion path rather than an index scan.
Recovery and validation
After the stuck transactions were cleared, both RUM indexes were rebuilt with REINDEX INDEX CONCURRENTLY.
The full index decreased from approximately 1.65 GiB to 1.06 GiB. Afterward:
- Metadata-only updates completed normally.
- Updates that changed indexed text completed normally.
- A search using
@@ and ordered using <=> selected the rebuilt full RUM index and completed in under 1 ms.
- The equivalent query with the partial-index predicates selected the rebuilt partial RUM index and also completed in under 1 ms.
- The original concurrent write workload has continued without stuck backends or lock chains.
Expected behavior
RUM insertion should complete or return an error. At minimum, a backend with ProcDiePending should reach an interrupt check and exit rather than spinning indefinitely.
Questions
- Is there a known write-side posting-tree/page-split corruption or infinite-loop issue in RUM 1.3.15 that matches this stack?
- Are there integrity checks that can detect this state before an update enters the loop?
- Would adding
CHECK_FOR_INTERRUPTS() to the relevant insertion/page-tree loops be appropriate as a defensive measure?
We do not yet have a standalone reproducer. We can collect additional sanitized debugger stacks or index metadata if maintainers suggest specific commands.
Summary
Several PostgreSQL backends performing concurrent
UPDATEs became stuck at 100% CPU while inserting into a RUMtsvectorindex. The backends did not respond topg_terminate_backend(): it returnedtrue, and debugger inspection showed bothInterruptPending = 1andProcDiePending = 1, but the processes continued spinning.Rebuilding the affected RUM indexes resolved the problem. The same write and indexed-search paths now complete in milliseconds under the original workload.
Environment
1.3rum_tsvector_opsGeneric schema/workload description
The affected table contains a
tsvectormaintained by aBEFORE INSERT OR UPDATEtrigger. The trigger combines several ordinary text fields using thesimpletext-search configuration.There were two RUM indexes on the same
tsvector:The workload performs concurrent updates to rows in this table. Even metadata-only updates cause the trigger to recompute the
tsvector.Observed behavior
UPDATEbackends consumed approximately one CPU core each indefinitely.pg_stat_activityshowed the spinning backends asactive, with no wait event.pg_terminate_backend(pid)returnedtrue, but the backends did not exit.The relevant portion of a representative stack was:
This indicates the loop was in the RUM insertion path rather than an index scan.
Recovery and validation
After the stuck transactions were cleared, both RUM indexes were rebuilt with
REINDEX INDEX CONCURRENTLY.The full index decreased from approximately 1.65 GiB to 1.06 GiB. Afterward:
@@and ordered using<=>selected the rebuilt full RUM index and completed in under 1 ms.Expected behavior
RUM insertion should complete or return an error. At minimum, a backend with
ProcDiePendingshould reach an interrupt check and exit rather than spinning indefinitely.Questions
CHECK_FOR_INTERRUPTS()to the relevant insertion/page-tree loops be appropriate as a defensive measure?We do not yet have a standalone reproducer. We can collect additional sanitized debugger stacks or index metadata if maintainers suggest specific commands.