RAG document loader segmentation fault on GCP Compute during large batch ingestion

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, and noac when 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

  1. 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.
  2. Can I keep the original n1-standard-4 instance and avoid OOM? You can, but you must split ingestion into smaller batches (< 1 GB) and reduce worker count, which adds operational overhead.
  3. Is switching to Cloud Storage (gs://) a better alternative? Yes. Directly reading from Cloud Storage via the gcsfs library avoids NFS latency and stale‑handle issues, though you must still provision enough memory for PDF parsing.
  4. 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.
  5. How can I detect memory corruption before a crash? Enable glibc malloc debugging (export MALLOC_CHECK_=3) and monitor the process logs for malloc(): memory corruption messages.

Related Topic Hub: Cloud Infrastructure Troubleshooting Hub

Related Articles