Weaviate controller manager crashes with segfault during high load inference

Problem – Controller‑manager crashes with segmentation fault under high‑load inference

In a multi‑node Weaviate cluster running on Kubernetes with GPU‑accelerated inference, the controller‑manager pod repeatedly terminates with a SIGSEGV during spikes of concurrent /v1/objects or /v1/graphql queries. The failure manifests as:


Segmentation fault (core dumped)
[signal SIGSEGV: segmentation fault] at 0x7f... in libtorch.so
panic: runtime error: invalid memory address or nil pointer dereference
    goroutine 12 [running]:
    github.com/weaviate/weaviate/modules/text2vec-contextionary/...

Typical operational impact includes:

  • API endpoints become unavailable for seconds to minutes.
  • Load balancers restart the pod, causing a brief warm‑up latency.
  • Downstream services (e.g., batch vectorisation jobs) fail with “controller manager aborts with fatal error”.

Root Cause – Interaction of GPU driver, libtorch ABI, and resource limits

The crash is not a generic Go panic; it originates from the native libtorch library loaded via cgo. Multiple evidence sources point to two tightly coupled failure modes:

  1. CUDA / libtorch ABI mismatch – A production GKE cluster (4‑node, NVIDIA A100) experienced SIGSEGV after a spike of ~2000 concurrent queries. The root cause was libtorch 1.13 compiled against CUDA 11.6 while the node driver reported CUDA 11.2 (GitHub #3124). The mismatch triggers illegal memory accesses inside libtorch.so when the inference engine allocates GPU buffers.
  2. Insufficient memory & OOM‑induced segfault – Helm charts with the default memoryLimit: 256Mi cause the controller‑manager to be OOM‑killed (GitHub #2987). The kernel then delivers a SIGSEGV when the process later dereferences a GPU buffer that has already been freed.
  3. Race between vectoriser and PostgreSQL connection pool – A nightly batch job that refreshes embeddings can race with the request dispatcher, leading to a nil‑pointer dereference in the controller‑manager’s dispatcher (Community Forum).

These conditions violate assumptions documented in the official “Modules & Inference” guide, which states that the controller‑manager expects a compatible CUDA driver (≥ 11.6 for libtorch 1.13) and sufficient pod resources to hold GPU buffers for the duration of a request.

Investigation – Step‑by‑step debugging

1. Capture pod state and logs


# Get the crashing pod name
kubectl get pods -n weaviate -l app=weaviate-controller-manager

# Stream logs with timestamps
kubectl logs -n weaviate weaviate-controller-manager-abcde -c controller-manager \
    --since=5m --timestamps

Typical log excerpt:


2024-06-28T14:12:03.421Z WARN  controller-manager: Failed to allocate GPU buffer: out of memory (CUDA error 2)
2024-06-28T14:12:03.422Z FATAL controller-manager: Segmentation fault (core dumped) at 0x7f3a9c2b4000 in libtorch.so

2. Verify CUDA driver version vs. libtorch ABI


# On a node where the pod was scheduled
kubectl exec -it -n weaviate weaviate-controller-manager-abcde -- nvidia-smi

# Expected output (example)
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 525.85.12    Driver Version: 525.85.12    CUDA Version: 12.0     |
+-----------------------------------------------------------------------------+

# Inside the container, inspect libtorch linkage
kubectl exec -it -n weaviate weaviate-controller-manager-abcde -- \
    ldd /usr/local/lib/libtorch.so | grep cuda
    libcuda.so.1 => /usr/lib/x86_64-linux-gnu/libcuda.so.1 (0x00007f3a9c2b4000)

If the driver reports CUDA 11.2 while libtorch expects ≥ 11.6, the mismatch is confirmed.

3. Check pod resource limits


kubectl get pod weaviate-controller-manager-abcde -n weaviate -o yaml \
    | grep -A4 resources
      resources:
        limits:
          cpu: "2"
          memory: 256Mi
          nvidia.com/gpu: "1"
        requests:
          cpu: "1"
          memory: 128Mi
          nvidia.com/gpu: "1"

Memory limits of 256 Mi are far below the typical GPU buffer footprint (≈ 1‑2 Gi per inference batch).

4. Examine PostgreSQL connection pool exhaustion


kubectl exec -it -n weaviate weaviate-controller-manager-abcde -- \
    curl -s http://localhost:8080/v1/.well-known/readyz
# Returns 503 if pool exhausted

Log line indicating pool exhaustion:


2024-06-28T14:12:05.001Z ERROR controller-manager: postgres: connection pool exhausted – max open connections reached

5. Core dump analysis (optional)


# Enable core dumps in the pod spec (if not already)
spec:
  securityContext:
    allowPrivilegeEscalation: true
    sysctls:
    - name: kernel.core_pattern
      value: /tmp/core.%e.%p.%t

# After crash, copy core file
kubectl cp weaviate-controller-manager-abcde:/tmp/core.controller-manager.* ./core.dump

# Analyze with gdb
gdb /usr/local/bin/controller-manager ./core.dump -ex "bt" -ex "quit"

The backtrace typically points to at::cuda::CUDAGraph::launch() inside libtorch, confirming the native layer failure.

Solution – Align CUDA stack, increase resources, and serialize vectoriser access

1. Pin compatible CUDA driver and libtorch version

Update the node image or install the required driver version (≥ 11.6 for libtorch 1.13). The official Weaviate Helm chart allows overriding the driver version via values.yaml:

Deploy the updated node pool (e.g., GKE node image cos-93-16623-104-0 with NVIDIA driver 525) and restart the controller‑manager pods.

2. Raise memory limits to accommodate GPU buffers

These values are consistent with the “Running Weaviate on Kubernetes” documentation, which suggests at least 2 Gi for GPU‑enabled inference pods.

3. Serialize access to the vectoriser and PostgreSQL pool

Introduce a lightweight Go mutex around the vectoriser call, or configure the controller‑manager’s vectorizerConcurrency flag (new in v1.22) to limit parallel inference jobs:

Alternatively, increase the PostgreSQL pool size in weaviate.yaml:

4. Re‑deploy with Helm


helm upgrade weaviate weaviate/weaviate \
  -f values.yaml \
  --namespace weaviate \
  --set image.tag=v1.23.0

The upgrade applies the driver, memory, and concurrency changes atomically.

Verification – Confirm stability after remediation

1. Smoke test high‑load inference


# Generate 2000 concurrent requests using wrk
wrk -t12 -c2000 -d30s -s ./weaviate_infer.lua http://weaviate.example.com/v1/graphql

Expected outcome: 2xx responses throughout the run, no pod restarts.

2. Monitor pod restarts and OOM events


kubectl get pods -n weaviate -w | grep controller-manager
# No RESTARTS column increment

kubectl logs -n weaviate weaviate-controller-manager-abcde -c controller-manager \
    | grep -i "Segmentation fault"
# No matches

3. Validate CUDA context initialization


kubectl exec -it -n weaviate weaviate-controller-manager-abcde -- \
    nvidia-smi
# Should show no errors and the correct driver version

4. Check PostgreSQL pool health


curl -s http://weaviate.example.com/v1/.well-known/readyz | grep "postgres"
# Should return "postgres: OK"

Prevention – Operational guardrails

  • Version lock: Pin both libtorch and CUDA driver versions in the Helm chart and node image. Use the gpu.driverVersion and gpu.libtorchVersion fields.
  • Resource budgeting: Set pod memory limits ≥ 2 Gi for any GPU‑enabled Weaviate component. Align with the “Running Weaviate on Kubernetes” recommendations.
  • Concurrency throttling: Use controllerManager.vectorizerConcurrency to bound simultaneous GPU jobs. Adjust based on observed GPU utilization (< 80 % is a safe target).
  • Health checks: Enable liveness/readiness probes that also verify the CUDA context (custom script invoking nvidia-smi).
  • Monitoring: Alert on container_cpu_usage_seconds_total spikes, container_memory_working_set_bytes approaching the limit, and any SIGSEGV events captured by the node’s kernel logs.
  • Core‑dump policy: Configure kernel.core_pattern to a persistent location and automate post‑mortem analysis for any future native crashes.

Related Topic Hub: Vector Databases Troubleshooting Hub

FAQ

  1. Why does the controller‑manager segfault only under load?
    The GPU buffer allocation path in libtorch is exercised only when the number of concurrent inference requests exceeds the available GPU memory. Under low load the code path is not triggered, hiding the ABI mismatch or OOM condition.
  2. How can I verify which libtorch version is loaded inside the container?
    Run strings /usr/local/lib/libtorch.so | grep "torch_version" or inspect the package manager metadata (e.g., dpkg -l | grep libtorch). The output should match the version declared in the Helm chart (gpu.libtorchVersion).
  3. Is it safe to increase maxOpenConns for PostgreSQL?
    Yes, provided the PostgreSQL instance can handle the additional connections. The controller‑manager’s pool defaults to 30; raising it to 100 (as shown) prevents “connection pool exhausted” errors during batch vectorisation.
  4. Can I run the controller‑manager on CPU‑only nodes?
    The inference module requires a CUDA context; without a GPU the request dispatcher will return an error before reaching libtorch. Use the no‑gpu module variant or disable inference on CPU‑only nodes.
  5. What kernel messages indicate an OOM‑induced segfault?
    Look for lines such as Out of memory: Kill process 12345 (controller-manager) score 987 or sacrifice child followed by SIGSEGV in the pod’s dmesg output. This pattern appears in the “Kubernetes pod OOM kills controller manager” issue.