PGlite wasm: multi-row `INSERT ... ON CONFLICT` under DELETE+INSERT churn probabilistically never returns — main-thread JIT spin, SIGTERM-proof, kill -9 only
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 5/5
- Tempo stimato
- Più di una settimana
- Idoneità per principianti
- 25/100
- Tipo di issue
- Bug
- Chiarezza
- Da chiarire
- Stato di attività
- Tranquilla
- Stack tecnologico
- bun, postgres, wasm
- Ambito
- databases
Direzione di ricerca
Ottenere la riproduzione completa E5 in pure-SQL e il database sanificato menzionati nel report, quindi eseguirli con PGlite 0.4.3 registrando le condizioni di blocco. Confrontare le esecuzioni senza l’indice HNSW e con la concorrenza dell’applicazione impostata a uno, come descritto nella matrice delle esclusioni. Il lavoro è completato quando il loop infinito è stato riprodotto e la sua causa è stata isolata a sufficienza da definire un test di regressione e una correzione circoscritta.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Version: @electric-sql/pglite 0.4.3 (bundled pgvector 0.8.1) · Runtime: Bun 1.3.14 · Platform: macOS 26.5.2, arm64 (Apple Silicon)
Summary
Under a sustained stream of per-page DELETE + multi-row INSERT ... ON CONFLICT
operations (~30 rows per INSERT, 18 columns including a vector(1024) column and
a generated tsvector column backed by a GIN index), PGlite probabilistically
enters an infinite loop inside the INSERT execution itself. The query never
returns, the JS main thread spins at ~99% CPU in JIT-generated code, SIGTERM is
never delivered (the event loop is wedged), and the process requires kill -9.
Reproduced 5 times on 2026-07-25: 4× inside the gbrain embedding pipeline
(with API-driven embedding), and — decisively — 1× in a pure offline SQL
storm with zero network, zero API, zero application code: just PGlite and raw
SQL against a copy of a real 47k-row database.
The decisive pure-SQL repro (E5)
Script shape (full script available; pseudocode):
const db = new PGlite('/path/to/copy-of-real-db', { extensions: { vector } });
// 70,555 rows with embeddings across 40,089 pages.
// For each page (20-way Promise-interleaved; PGlite single connection serializes):
// SELECT page's rows (embedding::text)
// DELETE FROM content_chunks WHERE page_id = $1
// INSERT INTO content_chunks (18 cols incl. $n::vector) VALUES (...N rows...)
// ON CONFLICT (page_id, chunk_index) DO UPDATE ...
Result: froze after ~19,300 of 40,089 pages (~48%), ~80 seconds into the
storm. CPU 97.7%, zero log growth, process wedged until kill -9.
Table/index state at freeze time (HNSW vector index had been dropped
beforehand — it is NOT required):
content_chunks_pkey, content_chunks_stale_idx, idx_chunks_embedding_image,
idx_chunks_embedding_null, idx_chunks_language, idx_chunks_page,
idx_chunks_page_index, idx_chunks_search_vector (GIN FTS, ~104MB),
idx_chunks_symbol_name, idx_chunks_symbol_qualified,
idx_content_chunks_edges_backfill, idx_content_chunks_symbol_lookup
Exclusion matrix (every hypothesis tested live, same database)
| Hypothesis | Result |
|---|---|
| Zero / NaN vectors breaking HNSW | ❌ 100 zero-vectors inserted fine; vectors at freeze all healthy (len=1024, nan=0, inf=0, nonzero=1024, max≈0.2) |
| Statically corrupted index | ❌ full replay of pending inserts exits cleanly |
| Poisoned page content | ❌ freeze pages (4 different ones across runs) re-run individually in seconds |
| Concurrency race | ❌ also freezes with application concurrency = 1 |
| API returning degraded vectors during rate-limit storms | ❌ vector health check at freeze instant: all healthy |
| HNSW index bloat / dead-tuple cliff | ❌ churn raises latency sub-linearly (153→405ms over 1500 rounds), no cliff; and a fresh REINDEX'd 438MB index froze at 11%; and with DROP INDEX idx_chunks_embedding it still froze |
| pgvector / HNSW involvement at all | ❌ falsified — freeze reproduced with the HNSW index absent |
The only common factor across all 5 freezes: a multi-row INSERT (31–33 rows,
with tsvector generated column + 4KB vector column) into the churned table,
executed by the PGlite wasm build. Page content, index type/presence/age,
concurrency, and vector health are all irrelevant.
Stack fingerprint (identical across all 5 captures)
sample(1) shows 100% of samples on the main thread in a ~50–90-frame chain of
anonymous JIT frames (addresses inside the JSC "JS JIT generated code" region,
which also hosts compiled wasm), via the Bun→JS trampoline (bun+0x32642b8).
All other threads idle. Zero open network connections (lsof). The inspector
protocol also dies: WS handshake succeeds but Debugger.enable/Debugger.pause
get zero responses — consistent with a tight JIT/wasm loop with no safepoint.
Real-world impact
In gbrain (which embeds via exactly this DELETE+INSERT pattern), this manifests
as gbrain embed --stale silently freezing mid-run — observed in production at
16% and 65% progress, one process spinning 92 minutes before manual kill.
SIGTERM-based supervisors cannot recover it.
Suspected areas (unverified pointers)
idx_chunks_search_vectorGIN FTS index — fastupdate pending-list flush
pathology under wasm during high-churn INSERT.- PGlite wasm memory growth / executor defect on large multi-row INSERTs with
wide rows (tsvector + vector(1024)).
Environment
- @electric-sql/pglite 0.4.3 (pgvector extension 0.8.1 bundled; upstream is 0.8.5)
- Bun 1.3.14, macOS 26.5.2 arm64
- Database: single PGlite data dir, ~532MB RSS at freeze, no autovacuum (PGlite),
never VACUUM/REINDEX except where noted - Forensics archive: 5×
samplestack dumps, 5× run logs, repro scripts
(happy to share the full bundle + a sanitized database copy)
- Lingua principale
- TypeScript
- Stelle
- 16.3k
- Fork
- 474
- Merge medio
- 23h 53m
- PR unite (30g)
- 53
Preparare l'ambiente
Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di electric-sql/pglite
-
Feature request: Nitro 3 support (`unwasm` export condition)Forse di nuovo libera Una pull request per questa issue è stata chiusa senza essere unita. Aperta
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 85/100
electric-sql/pglite#1120 · 16 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 5/5 Più di una settimana Idoneità per principianti 25/100
electric-sql/pglite#1200 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 54/100
electric-sql/pglite#1192 ·
I maintainer di solito rispondono entro 1 giorno
-
[BUG]: syncToFs() resolves immediately, without syncing, when another sync is already queuedForse già presa @PrabalParihar l’ha presa 11 giorni fa. Apertabug
Difficoltà 3/5 1-2 giorni Idoneità per principianti 35/100
electric-sql/pglite#1191 ·
I maintainer di solito rispondono entro 1 giorno
-
Feature request: live: allow pausing the notify triggers during bulk writes (e.g. pglite-sync applies) and refresh once on resumeForse già presa @tdrz l’ha presa 13 giorni fa. Apertaenhancement
Difficoltà 4/5 3-5 giorni Idoneità per principianti 25/100
electric-sql/pglite#1184 · 2 commenti · 1 assegnatario ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di electric-sql/pglite
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 74/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 66/100
open-webui/tether#52 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
voidzero-dev/void#835 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
AykutSarac/jsoncrack.com#616 ·
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
melgarafael/DeskcommCRM#4679 ·
I maintainer di solito rispondono entro 1 giorno