llm: rebuild postgres pgvector without AVX-512 — HNSW build SIGILLs the shared server on Zen2 #580

Closed
opened 2026-07-17 18:40:49 +00:00 by kert · 1 comment
Owner

The mirrored fhirworx/postgresql:latest image (Bitnami PG18, shared by gitea/nessie/polaris/llm) ships a pgvector 0.8.1 vector.so compiled with AVX-512, but the server host is a Ryzen 9 3950X (Zen 2 — no AVX-512).

Impact: CREATE INDEX ... USING hnsw and USING ivfflat fault with signal 4 (SIGILL) during the build — verified even on 3 rows. The fault kills every Postgres backend and drops the whole server (gitea/nessie/polaris included) into recovery (~15s). This is a shared-infra stability hazard, not just an llm failure.

Workaround shipped in P33: ANN index creation is gated behind [llm].build_ann_index (default false); the llm module runs on pgvector exact search, which works fine (query-time distance dispatch is correct — only the bulk index-build path faults). Exact search on the pilot (15,852 vectors) answers in ~1.2s.

Fix: rebuild the image's pgvector without AVX-512 / with working __builtin_cpu_supports dispatch. Cleanest low-risk path: a thin image FROM fhirworx/postgresql:latest that replaces vector.so with a build compiled -march=x86-64-v2 (or znver2). Then flip [llm].build_ann_index = true, stack llm index --force to build HNSW, and add ensure_hnsw to the indexer tail.

Acceptance:

  • CREATE INDEX ... USING hnsw on langchain_pg_embedding completes without crashing the server
  • gitea/nessie/polaris unaffected during an llm index build
  • [llm].build_ann_index = true + a corpus-scale query uses the HNSW index (EXPLAIN shows index scan)

Discovered during the P33 pilot run. Spec: docs/superpowers/specs/2026-07-16-llm-module-design.md (P33 build outcomes).

The mirrored `fhirworx/postgresql:latest` image (Bitnami PG18, shared by gitea/nessie/polaris/llm) ships a pgvector 0.8.1 `vector.so` compiled with **AVX-512**, but the server host is a **Ryzen 9 3950X (Zen 2 — no AVX-512)**. **Impact:** `CREATE INDEX ... USING hnsw` and `USING ivfflat` fault with **signal 4 (SIGILL)** during the build — verified even on 3 rows. The fault kills every Postgres backend and drops the whole server (gitea/nessie/polaris included) into recovery (~15s). This is a shared-infra stability hazard, not just an llm failure. **Workaround shipped in P33:** ANN index creation is gated behind `[llm].build_ann_index` (**default false**); the llm module runs on pgvector **exact search**, which works fine (query-time distance dispatch is correct — only the bulk index-build path faults). Exact search on the pilot (15,852 vectors) answers in ~1.2s. **Fix:** rebuild the image's pgvector without AVX-512 / with working `__builtin_cpu_supports` dispatch. Cleanest low-risk path: a thin image `FROM fhirworx/postgresql:latest` that replaces `vector.so` with a build compiled `-march=x86-64-v2` (or `znver2`). Then flip `[llm].build_ann_index = true`, `stack llm index --force` to build HNSW, and add `ensure_hnsw` to the indexer tail. **Acceptance:** - [ ] `CREATE INDEX ... USING hnsw` on `langchain_pg_embedding` completes without crashing the server - [ ] gitea/nessie/polaris unaffected during an llm index build - [ ] `[llm].build_ann_index = true` + a corpus-scale query uses the HNSW index (EXPLAIN shows index scan) Discovered during the P33 pilot run. Spec: `docs/superpowers/specs/2026-07-16-llm-module-design.md` (P33 build outcomes).
kert added this to the P33: LLM Foundation — Ollama, pgvector, embedding index milestone 2026-07-17 18:40:49 +00:00
kert added the llminfra labels 2026-07-17 18:40:49 +00:00
kert closed this issue 2026-07-17 20:13:45 +00:00
Author
Owner

Fixed in main as 666195e.

Root cause (confirmed from the kernel trap): the mirrored image's vector.so was built with pgvector's default -march=native on an AVX-512 host. On the Zen2 Ryzen 3950X, vector_norm() executed EVEX-encoded AVX-512 — vextractf64x2 @ offset 0x1fe11 and vcvtusi2sd @ 0x121d4 — which the CPU rejects (SIGILL), during hnsw/ivfflat index build. Query-time distance ops were legal AVX2, which is why exact search never faulted.

Fix: infra/images/postgresql.Dockerfile builds fhirworx/postgresql:pgvector FROM the :latest mirror, recompiling pgvector 0.8.1 (pinned + sha256-verified) with -march=x86-64-v3 (AVX2/FMA/BMI2, fully supported by Zen2) and swapping in the AVX-512-free vector.so. compose.yml (postgres build section) and deploy.sh (BUILDABLE) build it; [llm].build_ann_index now defaults true.

Verified on the live pilot: HNSW builds on the 15,852-vector index in ~2s with no crash; ivfflat too; EXPLAIN shows Index Scan using ix_embedding_hnsw; gitea/nessie/polaris unaffected. Data volume untouched (same PG 18.1, same extension version).

Build notes: Photon needs binutils+linux-api-headers; glibc-devel bumps glibc so the runtime stage installs glibc to match (else GLIBC_2.38 not found); never tdnf remove curl in the runtime (cascades out libldap.so.2).

Fixed in main as 666195e. **Root cause (confirmed from the kernel trap):** the mirrored image's `vector.so` was built with pgvector's default `-march=native` on an AVX-512 host. On the Zen2 Ryzen 3950X, `vector_norm()` executed EVEX-encoded AVX-512 — `vextractf64x2` @ offset 0x1fe11 and `vcvtusi2sd` @ 0x121d4 — which the CPU rejects (SIGILL), during hnsw/ivfflat index build. Query-time distance ops were legal AVX2, which is why exact search never faulted. **Fix:** `infra/images/postgresql.Dockerfile` builds `fhirworx/postgresql:pgvector` FROM the `:latest` mirror, recompiling pgvector 0.8.1 (pinned + sha256-verified) with `-march=x86-64-v3` (AVX2/FMA/BMI2, fully supported by Zen2) and swapping in the AVX-512-free `vector.so`. `compose.yml` (postgres build section) and `deploy.sh` (BUILDABLE) build it; `[llm].build_ann_index` now defaults true. **Verified on the live pilot:** HNSW builds on the 15,852-vector index in ~2s with no crash; ivfflat too; `EXPLAIN` shows `Index Scan using ix_embedding_hnsw`; gitea/nessie/polaris unaffected. Data volume untouched (same PG 18.1, same extension version). Build notes: Photon needs `binutils`+`linux-api-headers`; `glibc-devel` bumps glibc so the runtime stage installs `glibc` to match (else `GLIBC_2.38 not found`); never `tdnf remove curl` in the runtime (cascades out `libldap.so.2`).
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: homelab/stack#580