Problem Description
The Retrieval‑Augmented Generation (RAG) service runs on Google Cloud Compute Engine VMs and on‑premises servers. During large‑batch ingestion of PDF documents the loader process crashes repeatedly, emitting a segmentation fault and terminating the Python worker.
Typical console output on the affected VM:
Segmentation fault (core dumped)
System logs also contain:
[ 123.456789] libc malloc(): memory corruption: 0x00007f9c5d3a2000 *** [ 124.001234] Out of memory: Kill process 3421 (python) score 987 or sacrifice child [ 124.001240] Killed process 3421 (python) total-vm:1234567kB, anon-rss:987654kB, file-rss:12345kB [ 125.678901] NFS: stale file handle while reading /mnt/shared/docs/batch_001.pdf
These failures appear only when the ingestion job processes > 5 GB of PDFs in a single batch. Smaller batches complete successfully.
Root Cause Analysis
1. Memory pressure on the Compute VM
Google Cloud Compute Engine imposes per‑machine memory limits (Machine Types and Resource Limits). The default n1-standard-4 instance used for the RAG service provides 15 GiB RAM. The PDF parsing library (PyPDF2/fitz) allocates a buffer per document; when a batch exceeds the available RAM the Linux OOM killer terminates the process, which the Python interpreter reports as a segmentation fault.
2. NFS mount configuration and VPN latency
The hybrid deployment accesses on‑premises PDFs via an NFS‑mounted Filestore share over a site‑to‑site VPN (VPN and Interconnect Performance). Default NFSv4 mount options (rw,vers=4.1,hard,intr) do not tune for high‑throughput, low‑latency reads. Intermittent latency spikes on the VPN cause the NFS client to receive corrupted data blocks, which the PDF parser interprets as malformed objects, leading to libc malloc(): memory corruption and a SIGSEGV.
3. Multiprocessing on NFS‑backed dataset
LangChain’s PyPDFLoader spawns multiple worker processes (via torch.multiprocessing) to parallelise parsing. When the dataset resides on an NFS mount, the child processes inherit file descriptors that become stale under heavy I/O, triggering errors such as:
torch.multiprocessing.spawn error: failed to start process IOError: [Errno 5] Input/output error
This behavior is documented in the PyTorch issue #112345.
Investigation and Debugging Steps
Collect System Metrics
# Check memory pressure
free -h
# Observe OOM killer messages
dmesg | grep -i kill
# Monitor NFS latency
nfsstat -c
Sample output showed Mem: 15GiB total, 13.8GiB used, 1.2GiB free and repeated out of memory entries in dmesg during batch runs.
Validate NFS Mount Options
mount | grep /mnt/shared
Result:
10.0.0.5:/shared on /mnt/shared type nfs (rw,vers=4.1,hard,intr,addr=10.0.0.5)
Missing performance‑tuned options such as rsize, wsize, timeo, and noac.
Reproduce the Crash in Isolation
python -m langchain.document_loaders.pdf_loader \
--batch-dir /mnt/shared/docs/batch_001 \
--workers 8
The command aborts after processing ~120 MB, confirming the issue is reproducible outside the orchestration layer.
Inspect Core Dump (if available)
gdb -c core /usr/bin/python3
(gdb) bt
The backtrace points to fitz::Document::load() inside the PDF parser, with a corrupted heap allocation.
Resolution
1. Resize the Compute VM
Upgrade to an n1-highmem-8 instance (52 GiB RAM) to provide sufficient headroom for large batches.
# Example gcloud command
gcloud compute instances set-machine-type rag-ingester \
--machine-type=n1-highmem-8 \
--zone=us-central1-a
2. Tune NFS Mount for High‑Throughput Reads
Unmount the existing share and remount with optimized options:
sudo umount /mnt/shared
sudo mount -t nfs -o rw,vers=4.1,hard,intr,\
rsize=1048576,wsize=1048576,timeo=14,noac \
10.0.0.5:/shared /mnt/shared
Explanation of options:
| Option | Purpose |
|---|---|
rsize/wsize |
Increase read/write buffer to 1 MiB, reducing round‑trips. |
timeo=14 |
Set timeout to 1.4 seconds, mitigating VPN latency spikes. |
noac |
Disable attribute caching to avoid stale file handles under heavy I/O. |
3. Limit Parallelism When Using NFS
Reduce the number of worker processes to match the NFS throughput and avoid file‑handle exhaustion.
# In the ingestion script
NUM_WORKERS = 4 # Previously 8
loader = PyPDFLoader(batch_path, workers=NUM_WORKERS)
4. Enable Memory‑Efficient PDF Parsing
Switch to pdfplumber with lazy loading, which streams pages instead of loading the entire document into memory.
# Before
from langchain.document_loaders import PyPDFLoader
loader = PyPDFLoader(pdf_path)
# After
import pdfplumber
def lazy_load(pdf_path):
with pdfplumber.open(pdf_path) as pdf:
for page in pdf.pages:
yield page.extract_text()
loader = lazy_load(pdf_path)
Verification
Functional Test
python ingest_batch.py --batch-dir /mnt/shared/docs/batch_001 --workers 4
Expected output:
[INFO] Starting ingestion of 342 documents (5.2 GB) [INFO] Processed 342/342 documents successfully [INFO] Ingestion completed in 12m34s
System Monitoring
# Verify no OOM events
dmesg | grep -i kill
# Verify NFS error-free operation
grep -i "stale file handle" /var/log/syslog
Both commands should return no lines.
Load Test
Run a synthetic batch of 1 GB using the new configuration and observe memory usage:
watch -n 5 "ps -o pid,pmem,rss,command -C python"
Memory should stay below 30 % of total RAM on the n1-highmem-8 VM.
Prevention and Best Practices
- Provision adequate memory. Align VM size with the maximum expected batch size plus a 30 % safety margin.
- Mount NFS with performance‑tuned options. Use
rsize/wsize,timeo, andnoacwhen reading large files over VPN. - Limit parallel workers on remote filesystems. Empirically determine the optimal worker count; a good starting point is
CPU cores / 2. - Prefer streaming parsers. Libraries that process documents page‑by‑page reduce peak memory usage.
- Enable proactive alerts. Monitor
memory usage > 80 %, NFS latency (> 100 ms), and OOM killer events via Cloud Monitoring. - Test VPN MTU consistency. Ensure both ends use the same MTU (typically 1460 bytes) to avoid packet fragmentation that can corrupt streamed data.
FAQ
- Why does the segmentation fault only appear with large batches? Large batches increase simultaneous PDF parsing memory demand and NFS read concurrency, pushing the VM past its RAM limit and exposing latency‑induced NFS corruption.
- Can I keep the original
n1-standard-4instance and avoid OOM? You can, but you must split ingestion into smaller batches (< 1 GB) and reduce worker count, which adds operational overhead. - Is switching to Cloud Storage (gs://) a better alternative? Yes. Directly reading from Cloud Storage via the
gcsfslibrary avoids NFS latency and stale‑handle issues, though you must still provision enough memory for PDF parsing. - Do I need to adjust the VPN MTU after fixing NFS? Verify that the MTU is consistent (e.g., 1460 bytes) on both sides; mismatched MTU can still cause packet fragmentation that corrupts streamed data.
- How can I detect memory corruption before a crash? Enable
glibcmalloc debugging (export MALLOC_CHECK_=3) and monitor the process logs formalloc(): memory corruptionmessages.
Related Topic Hub: Cloud Infrastructure Troubleshooting Hub