Problem Description
During a rolling update of a production Meta LLaMA RAG service, the retrieval‑augmented generation pipeline intermittently fails to retrieve embeddings. The failure manifests as:
grpc deadline exceeded: context deadline exceeded while attempting to query vector storeVectorStoreError: connection refused – unable to establish TCP connection to Faiss endpointTimeoutError: failed to retrieve embeddings within 30s – connection pool exhaustedCircuitBreakerOpenException – vector store circuit breaker opened due to consecutive timeouts during rolling update
Operational impact includes increased latency, failed user queries, and temporary service degradation lasting 2–3 minutes per rollout.
Root Cause Analysis
The issue originates from the interaction between Kubernetes rolling update semantics and the vector store connection handling defined in the Meta LLaMA deployment configuration:
- Pod termination without graceful socket release – When old model pods are evicted, the
preStophook is missing, so open gRPC connections to the vector store (FAISS or Milvus) are abruptly closed. - Connection‑pool saturation – New pods start immediately and begin populating their own connection pools. The vector store sees a sudden surge of concurrent connection attempts while the old sockets are still in
CLOSE_WAIT, exhausting the server’smax_connectionslimit (default 1024 for FAISS, 5000 for Milvus). - Readiness probe misconfiguration – The model pod reports
Readybefore it has successfully established a stable vector‑store client, allowing traffic to be routed to a pod that cannot yet query the store. - Timeout settings too low – The default
vector_store.timeout_msof 30000 ms (30 s) is insufficient when the store is under load from the rollout, causing the client to abort before the server can accept new connections.
This chain of events matches the community reports in the GitHub issue “Vector store timeout during rolling update” and the production incident where “grpc deadline exceeded” errors persisted for 2‑3 minutes.
Investigation and Debugging
Below is a reproducible debugging workflow that was used to isolate the problem.
1. Log inspection
2026-08-25T14:02:31.112Z model-pod-5c9f7c9d8f-9kzlm INFO Starting inference request id=abc123
2026-08-25T14:02:31.115Z model-pod-5c9f7c9d8f-9kzlm ERROR VectorStoreError: connection refused - unable to establish TCP connection to faiss-service:8080
2026-08-25T14:02:31.120Z model-pod-5c9f7c9d8f-9kzlm WARN grpc deadline exceeded: context deadline exceeded while attempting to query vector store
2026-08-25T14:02:31.125Z kubelet INFO Pod model-pod-5c9f7c9d8f-9kzlm terminating (reason: RollingUpdate)
2026-08-25T14:02:31.130Z faiss-service INFO New connection from 10.2.3.45:54321 rejected: max connections reached
2. Connection‑pool metrics
Using kubectl exec on the vector‑store pod:
kubectl exec -it faiss-service-0 -- bash
# inside container
netstat -anp | grep :8080 | wc -l
# => 1024 (maxed out)
3. Verify readiness probe timing
kubectl get pod model-pod-5c9f7c9d8f-9kzlm -o yaml | grep readinessProbe -A4
...
readinessProbe:
httpGet:
path: /healthz
port: 8081
initialDelaySeconds: 5
periodSeconds: 10
failureThreshold: 3
The probe only checks the HTTP health endpoint, not the vector‑store client status.
4. Capture traffic during rollout
tcpdump -i eth0 port 8080 -w /tmp/faiss-rollout.pcap
# After rollout, analyze with Wireshark:
# Observe many SYN packets followed by RST from the Faiss server.
5. Compare configuration defaults vs. tuned values
| Parameter | Default | Tuned |
|---|---|---|
| vector_store.timeout_ms | 30000 | 60000 |
| vector_store.max_pool_size | 20 | 50 |
| faiss.max_connections | 1024 | 2048 |
| readinessProbe.initialDelaySeconds | 5 | 30 |
Resolution
The fix consists of three coordinated changes: graceful pod shutdown, connection‑pool scaling, and readiness‑probe enhancement.
1. Add a preStop hook to drain vector‑store sockets
Before (deployment snippet):
apiVersion: apps/v1
kind: Deployment
metadata:
name: llama-model
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
spec:
containers:
- name: model
image: ghcr.io/facebookresearch/llama:2.0
ports:
- containerPort: 8080
After – add preStop that closes the gRPC client and waits for in‑flight requests:
apiVersion: apps/v1
kind: Deployment
metadata:
name: llama-model
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
spec:
containers:
- name: model
image: ghcr.io/facebookresearch/llama:2.0
ports:
- containerPort: 8080
lifecycle:
preStop:
exec:
command: ["/bin/sh","-c","python /app/drain_vector_store.py && sleep 10"]
The drain_vector_store.py script closes the client pool and optionally sends a health‑check /drain request to the vector store.
2. Increase vector‑store connection limits
For FAISS (via faiss.conf) and Milvus (via milvus.yaml), raise the max connections:
# faiss.conf
max_connections = 2048
# milvus.yaml
etcd:
max_conn: 5000
3. Extend readiness probe to validate vector‑store connectivity
Replace the simple HTTP health check with a script that attempts a lightweight vector‑store query.
readinessProbe:
exec:
command:
- /bin/sh
- -c
- |
python -c "
import grpc, faiss_pb2_grpc, faiss_pb2
channel = grpc.insecure_channel('faiss-service:8080', options=[('grpc.keepalive_time_ms', 10000)])
try:
stub = faiss_pb2_grpc.FaissStub(channel)
stub.Ping(faiss_pb2.PingRequest())
exit(0)
except Exception:
exit(1)
"
initialDelaySeconds: 30
periodSeconds: 10
failureThreshold: 3
4. Adjust client‑side timeout and pool size
Update the model’s configuration (see deployment_config.md):
vector_store:
timeout_ms: 60000 # increased from 30000
max_pool_size: 50 # increased from 20
retry:
max_attempts: 3
backoff_ms: 2000
Verification
After applying the changes, perform the following checks:
- Rolling update test – trigger a rollout in a staging namespace and watch logs for the absence of timeout errors.
- Connection metrics – query the vector‑store’s connection count before, during, and after rollout:
kubectl exec -it faiss-service-0 -- curl http://localhost:8080/metrics | grep grpc_server_connections
# Expected: peak < 1500 (well below 2048 limit)
kubectl get pods -l app=llama-model -o jsonpath='{.items[*].status.conditions[?(@.type=="Ready")].status}'
# Expected output: "True" for all pods after ~30 s
curl -X POST http://llama-gateway/api/rag -d '{"query":"What is the return policy?"}' -H "Content-Type: application/json"
# Response should include "retrieved_documents": [...]
Operational Experience
During the initial investigation, the most misleading symptom was the rapid appearance of grpc deadline exceeded errors, which suggested a client‑side timeout rather than a server‑side connection‑pool exhaustion. Only after capturing a packet trace did we see a flood of SYN packets being reset by the vector store.
Another surprise was that the default Kubernetes maxUnavailable: 0 strategy, while preserving overall replica count, still allowed a momentary dip to zero available vector‑store connections because the vector store itself became a bottleneck.
Adding the preStop hook not only closed sockets cleanly but also gave the service mesh (e.g., Istio) time to drain in‑flight requests, eliminating the “CircuitBreakerOpenException” observed in the incident logs.
Best Practices and Prevention
- Graceful shutdown: Always define a
preStophook that closes external client pools and waits for pending RPCs. - Readiness validation: Extend readiness probes to cover downstream dependencies, especially stateful services like vector stores.
- Capacity planning: Size
max_connectionson FAISS/Milvus to accommodate the peak connection count during rollouts (typicallyreplicas × max_pool_size × surge_factor). - Timeout tuning: Set
vector_store.timeout_msto at least twice the expected worst‑case latency under load. - Observability: Export connection‑pool and circuit‑breaker metrics (e.g.,
grpc_server_connections,circuit_breaker_state) and alert on sudden spikes. - Canary rollouts: Deploy a single new pod first, verify vector‑store health, then proceed with the full rolling update.
Related Questions
- Why does the vector‑store timeout only during rolling updates?
Because the update introduces a burst of simultaneous connection attempts while old pods still hold sockets, exceeding the store’s connection limit and causing timeouts. - How can I verify which cipher suite was negotiated for gRPC between the model and vector store?
Rungrpcurl -v -proto your.proto your-vector-store:8080 YourService/Methodand inspect the TLS handshake details in the verbose output. - Can connection pooling affect gRPC handshake behavior?
Yes. An exhausted pool forces the client to open new TCP connections, which may be rejected if the server’smax_connectionsis reached, leading to handshake failures. - Why does OpenSSL succeed while the application traffic fails?
OpenSSL tests a single connection without pooling, whereas the application reuses a pool that can become saturated during rollout. - What Kubernetes settings help avoid zero‑available vector‑store connections?
ConfiguremaxSurgeandmaxUnavailablesuch that the total number of model pods never exceeds the vector store’s capacity, and usepreStophooks to drain connections.
Related Topic Hub: LLM Systems Troubleshooting Hub