fix(llm): rebuild pgvector without AVX-512 so HNSW builds on Zen2 (closes #580)

Upstream Bitnami vector.so was built -march=native on an AVX-512 host;
vector_norm's EVEX AVX-512 (vextractf64x2/vcvtusi2sd) SIGILLs the Ryzen
3950X during hnsw/ivfflat index builds, crashing the shared postgres.
infra/images/postgresql.Dockerfile rebuilds pgvector -march=x86-64-v3
FROM the mirror; compose + deploy.sh build fhirworx/postgresql:pgvector.
HNSW verified on the 15852-vector pilot (~2s, index scan). Flip
[llm].build_ann_index default to true.
This commit is contained in:
kert
2026-07-17 16:09:13 -04:00
parent 14d279bdfd
commit 666195e3fd
6 changed files with 72 additions and 20 deletions

View File

@@ -136,8 +136,15 @@ services:
restart: unless-stopped restart: unless-stopped
postgres: postgres:
image: ${IMAGE_PREFIX:-fhirworx}/postgresql:latest # Patched pgvector (AVX-512-free) built FROM the upstream mirror — the
# stock vector.so SIGILLs this Zen2 host on hnsw/ivfflat index builds (#580).
image: ${IMAGE_PREFIX:-fhirworx}/postgresql:pgvector
pull_policy: if_not_present pull_policy: if_not_present
build:
context: .
dockerfile: infra/images/postgresql.Dockerfile
args:
BASE: ${IMAGE_PREFIX:-fhirworx}/postgresql:latest
container_name: postgres container_name: postgres
networks: networks:
- storage - storage

View File

@@ -64,6 +64,8 @@ declare -A BUILDABLE=(
[zotero]="${PREFIX}/zotero:${HEAVY}" [zotero]="${PREFIX}/zotero:${HEAVY}"
[docs]="${PREFIX}/docs:${SHA}" [docs]="${PREFIX}/docs:${SHA}"
[api]="${PREFIX}/api:${SHA}" [api]="${PREFIX}/api:${SHA}"
# Patched pgvector (AVX-512-free) built FROM the postgresql:latest mirror (#580).
[postgres]="${PREFIX}/postgresql:pgvector"
) )
# ── Pre-flight: check local images ───────────────────────────────── # ── Pre-flight: check local images ─────────────────────────────────

View File

@@ -91,20 +91,23 @@ minus the task server it feared.
P33 landed on branch `llm-p33`. Two infrastructure findings changed the plan: P33 landed on branch `llm-p33`. Two infrastructure findings changed the plan:
**pgvector ANN index build crashes the shared Postgres (AVX-512 SIGILL).** The **pgvector ANN index build crashed the shared Postgres (AVX-512 SIGILL) —
mirrored `fhirworx/postgresql:latest` image (Bitnami PG18, shared by fixed (#580).** The mirrored `fhirworx/postgresql:latest` image (Bitnami PG18,
gitea/nessie/polaris) ships a pgvector 0.8.1 `vector.so` built with AVX-512, shared by gitea/nessie/polaris) shipped a pgvector 0.8.1 `vector.so` built with
but the server host is a Ryzen 9 3950X (Zen 2, no AVX-512). `CREATE INDEX … `-march=native` on an AVX-512 build host, but the server is a Ryzen 9 3950X
USING hnsw` **and** `ivfflat` fault with signal 4 (SIGILL) — even on 3 rows — (Zen 2, no AVX-512). `CREATE INDEX … USING hnsw` **and** `ivfflat` faulted with
and the fault kills every backend, dropping the whole server (and its other signal 4 (SIGILL) — even on 3 rows — because `vector_norm()` executed
databases) into recovery. Index-free **exact search works fine** (query-time EVEX-encoded AVX-512 (`vextractf64x2`, `vcvtusi2sd`); the fault killed every
distance dispatch is correct; only the bulk index-build path faults). Decision: backend and dropped the whole server (and its other databases) into recovery.
ANN index creation is gated behind `[llm].build_ann_index` (**default false**); Query-time distance ops were compiled as legal AVX2, which is why exact search
the module ships on exact search. Restoring HNSW needs the image's pgvector never faulted. **Fixed** by `infra/images/postgresql.Dockerfile`: a patched
rebuilt without AVX-512 (thin image `FROM fhirworx/postgresql:latest` replacing `fhirworx/postgresql:pgvector` image built `FROM` the mirror that rebuilds
`vector.so` with a `-march=x86-64-v2`/`znver2` build) — filed as a P33 pgvector with `-march=x86-64-v3` (AVX2/FMA, fully supported by Zen 2) and swaps
follow-up issue. Exact search on the pilot (15,852 vectors) answers in ~1.2 s in the AVX-512-free `vector.so`. Verified: HNSW builds on the 15,852-vector
for two queries, so this is not urgent at pilot scale. pilot in ~2 s with no crash, queries use `Index Scan using ix_embedding_hnsw`,
and dependents are unaffected. `[llm].build_ann_index` now **defaults true**;
set it false to fall back to exact search. `compose.yml` and `deploy.sh` build
the patched image from the mirror base.
**Embed model bake-off (#564).** Retrieval-proxy metric (comment abstract as **Embed model bake-off (#564).** Retrieval-proxy metric (comment abstract as
query against its own pooled chunks, 120 pilot comments): query against its own pooled chunks, 120 pilot comments):

View File

@@ -0,0 +1,40 @@
# Rebuild pgvector without AVX-512.
#
# The upstream Bitnami image (fhirworx/postgresql:latest — PostgreSQL 18.1 on
# VMware Photon) ships a vector.so built with pgvector's default
# OPTFLAGS=-march=native on an AVX-512-capable build host. On this server's
# Ryzen 9 3950X (Zen 2, no AVX-512) the hnsw/ivfflat index BUILD path SIGILLs:
# vector_norm() executes EVEX-encoded AVX-512 (vextractf64x2 @ 0x1fe11,
# vcvtusi2sd @ 0x121d4) that the CPU rejects, which crashes the whole server
# (every backend, gitea/nessie/polaris included, drops into recovery).
# Query-time distance ops were compiled as legal AVX2, so exact search works —
# only the index build faults. Rebuilding pgvector with -march=x86-64-v3
# (AVX2/FMA/BMI2, fully supported by Zen 2) keeps SIMD while dropping every
# AVX-512 encoding. See issue #580.
ARG BASE=fhirworx/postgresql:latest
ARG PGVECTOR_VERSION=0.8.1
ARG PGVECTOR_SHA256=a9094dfb85ccdde3cbb295f1086d4c71a20db1d26bf1d6c39f07a7d164033eb4
FROM ${BASE} AS build
ARG PGVECTOR_VERSION
ARG PGVECTOR_SHA256
USER 0
# hadolint ignore=DL3041
RUN tdnf install -y gcc binutils make glibc-devel linux-api-headers tar gzip curl && \
curl -fsSL "https://github.com/pgvector/pgvector/archive/refs/tags/v${PGVECTOR_VERSION}.tar.gz" \
-o /tmp/pgvector.tar.gz && \
echo "${PGVECTOR_SHA256} /tmp/pgvector.tar.gz" | sha256sum -c - && \
tar -xzf /tmp/pgvector.tar.gz -C /tmp && \
make -C "/tmp/pgvector-${PGVECTOR_VERSION}" OPTFLAGS="-march=x86-64-v3" with_llvm=no && \
make -C "/tmp/pgvector-${PGVECTOR_VERSION}" install
FROM ${BASE}
USER 0
# Installing glibc-devel above pulled a glibc update, so the rebuilt vector.so
# links against the newer glibc; bump the runtime glibc to match (glibc is
# backward-compatible, so the existing postgres binaries keep working). This
# installs only glibc — no build tools reach the final image.
# hadolint ignore=DL3041
RUN tdnf install -y glibc && tdnf clean all
COPY --from=build /opt/bitnami/postgresql/lib/vector.so /opt/bitnami/postgresql/lib/vector.so
USER 1001

View File

@@ -106,10 +106,10 @@ ollama = "http://127.0.0.1:11434" # host-side default; containers override v
embed_model = "nomic-embed-text" # 768-dim; bake-off (P33 #564) may revise embed_model = "nomic-embed-text" # 768-dim; bake-off (P33 #564) may revise
instruct_model = "llama3.1:8b" # bake-off may revise instruct_model = "llama3.1:8b" # bake-off may revise
embed_dim = 768 embed_dim = 768
# ANN index build (hnsw/ivfflat) SIGILLs on this Zen2 host — the mirrored # HNSW index build. Safe since the postgres image ships an AVX-512-free
# postgres image's pgvector is AVX-512. Leave false until the image is # pgvector rebuild (infra/images/postgresql.Dockerfile, #580); before that it
# rebuilt; exact search works regardless. # SIGILLed this Zen2 host. Set false to fall back to exact search.
build_ann_index = false build_ann_index = true
pg_host = "127.0.0.1" # host-side default; containers set LLM_PG_HOST=postgres pg_host = "127.0.0.1" # host-side default; containers set LLM_PG_HOST=postgres
pg_port = 5432 pg_port = 5432
pg_db = "llm" pg_db = "llm"

View File

@@ -16,7 +16,7 @@ class TestLoad:
assert cfg.embed_dim == 768 assert cfg.embed_dim == 768
assert cfg.pg_host == "127.0.0.1" assert cfg.pg_host == "127.0.0.1"
assert cfg.pg_db == "llm" assert cfg.pg_db == "llm"
assert cfg.build_ann_index is False assert cfg.build_ann_index is True
def test_env_overrides(self, monkeypatch): def test_env_overrides(self, monkeypatch):
monkeypatch.setenv( monkeypatch.setenv(