Skip to main content
Version: 1.5.0

WAR Extraction to tmpfs Optimization

Overview

This optimization improves DataHub GMS startup performance by 15-30% through two key techniques:

  1. Extracting the WAR to a tmpfs (RAM disk) - Eliminates nested JAR overhead
  2. Using Spring Boot's classpath.idx for deterministic class loading - Ensures classes load in consistent order

How It Works

1. WAR Extraction to tmpfs

Instead of running Java from the packaged WAR directly:

Traditional: java -jar war.war
→ Java decompresses nested JARs on first class load
→ Slow filesystem I/O
→ Filesystem order randomness affects startup

This optimization extracts to RAM disk first:

Optimized: Extract WAR → /tmp/gms/extraction (tmpfs)
→ Read classes from RAM
→ No decompression overhead
→ 2-3× faster class loading

2. Deterministic Class Loading with classpath.idx

Spring Boot 3.2+ includes BOOT-INF/classpath.idx - a pre-computed ordered list of all JARs and their dependencies.

Without optimization:

  • Classpath built from filesystem directory listing
  • Filesystem order varies between boots (some filesystems randomize)
  • May affect class resolution order if there are duplicate classes
  • Variable startup times depending on filesystem state

With optimization:

  • Uses Spring's pre-computed classpath.idx ordering
  • Same class loading order every boot
  • Consistent performance
  • Deterministic behavior for reproducible environments

How it works:

# classpath.idx format:
- "BOOT-INF/lib/spring-core-6.0.jar"
- "BOOT-INF/lib/spring-context-6.0.jar"
- "BOOT-INF/lib/spring-data-commons-3.0.jar"
... (100+ entries in dependency order)

# Extracted to absolute paths:
/tmp/gms-work/BOOT-INF/classes (application classes first)
/tmp/gms-work/BOOT-INF/lib/spring-core-6.0.jar
/tmp/gms-work/BOOT-INF/lib/spring-context-6.0.jar
/tmp/gms-work/BOOT-INF/lib/spring-data-commons-3.0.jar
... (all as single colon-separated classpath)

Performance Impact

MetricImprovement
Startup Time15-30% faster
Class Loading2-3× faster (RAM vs filesystem)
ConsistencyDeterministic ordering, no random variations
Memory Overhead+150-300MB temporary (freed after startup completes)

Example Timing

Without optimization (filesystem WAR):
- WAR decompression: 8-15s
- Class discovery: 5-10s
- Total startup: ~20-30s

With optimization (tmpfs extraction):
- WAR extraction to RAM: 2-3s
- Class discovery (from classpath.idx): 1-2s
- Total startup: ~15-20s

Net gain: 5-15 seconds faster

Configuration

Enable in Helm

Add to your values.yaml:

# Enable WAR extraction to tmpfs for faster startup
extractJarEnabled: true

Or via command line:

helm install datahub ... --set extractJarEnabled=true

What Gets Created

When extractJarEnabled: true:

  1. tmpfs emptyDir volume (1Gi, Memory-backed)

    • Mounted at /tmp/gms-work in the container
    • Automatically cleaned up after pod termination
  2. EXTRACT_JAR_ENABLED environment variable

    • Tells startup script to use extraction
    • Triggers classpath.idx loading
  3. Startup logging

    • WAR size logged for validation
    • Available RAM checked for safety
    • Extraction time measured

Requirements

RequirementMinimumRecommendedNotes
Available RAM500MB2GB+Per pod; extraction is temporary
WAR File SizeN/A< 500MBExceeding 1Gi tmpfs limit will fail
Spring Boot Version3.2+LatestRequires classpath.idx support
Kubernetes1.20+1.24+For reliable emptyDir medium: Memory

Pre-Flight Checks

The startup script performs these checks:

[STARTUP] JAR extraction enabled. WAR size: 250MB, Available RAM: 7200MB
[STARTUP] Extracting WAR to tmpfs: /tmp/gms-work
[STARTUP] Generating deterministic classpath from BOOT-INF/classpath.idx
[STARTUP] WAR extracted in 2843ms

Warning Conditions

⚠️ WAR Size Warning (> 1Gi):

[WARN] WAR size (1200MB) exceeds tmpfs limit (1Gi). Extraction may fail

Action: Increase tmpfs sizeLimit in values.yaml or reduce WAR size

⚠️ Low RAM Warning (< 500MB):

[WARN] Low available RAM (256MB). Extraction may fail or trigger swap

Action: Allocate more resources to the pod or disable optimization

Architecture Details

Classpath.idx Processing

The startup script processes classpath.idx in 4 steps:

Step 1: Convert to absolute paths

- "BOOT-INF/lib/spring-core.jar"

/tmp/gms-work/BOOT-INF/lib/spring-core.jar

Step 2: Prepend application classes

/tmp/gms-work/BOOT-INF/classes (application code - loaded first)
/tmp/gms-work/BOOT-INF/lib/... (library JARs - in dependency order)

Step 3: Join into single classpath

/tmp/gms-work/BOOT-INF/classes:/tmp/gms-work/BOOT-INF/lib/jar1.jar:/tmp/gms-work/BOOT-INF/lib/jar2.jar:...

Step 4: Create Java argfile

# Avoids shell variable size limits (32KB-256KB depending on system)
cat > java.args <<EOF
-cp
/tmp/gms-work/BOOT-INF/classes:/tmp/gms-work/BOOT-INF/lib/...
com.linkedin.gms.GMSApplication
EOF

java @java.args # Load from file instead of command line

Troubleshooting

Startup Fails: "Missing BOOT-INF/classpath.idx"

Cause: WAR is not a Spring Boot executable archive or Spring Boot < 3.2

Solution:

  • Verify Spring Boot version ≥ 3.2
  • Disable optimization: extractJarEnabled: false
  • Check WAR packaging in build pipeline

Extraction Hangs or Times Out

Cause: Not enough RAM or disk space

Solution:

# Increase pod resources
resources:
requests:
memory: 4Gi
limits:
memory: 6Gi

tmpfs Mount Permission Denied

Cause: Security context doesn't allow tmpfs mounting

Solution:

podSecurityContext:
fsGroup: 1000

securityContext:
runAsNonRoot: true
runAsUser: 1000

High Memory Usage After Startup

Expected: Temporary spike during extraction (freed after startup completes)

Monitor: Watch for sustained high memory after startup settles. If sustained, check:

  • Heap size configuration (may need tuning)
  • Garbage collection logs
  • Application memory leaks

Disabling the Optimization

If you need to disable for debugging or compatibility:

extractJarEnabled: false # Default

The container will run normally without extraction (standard startup path).

Performance Comparison

Cold Start (Pod Creation)

ConfigurationTimeWAR SizeRAM Used
Standard (no extraction)25-35s250MB1.2GB baseline
With tmpfs extraction18-25s250MB1.2GB + 250MB (temporary)
Improvement+25-30%

Warm Start (Existing Pod)

Both configurations are similar once JVM is loaded. Main improvement is initial startup only.

Kubernetes Best Practices

Resource Requests/Limits

Ensure adequate resources for extraction:

resources:
requests:
memory: 2Gi
cpu: 500m
limits:
memory: 4Gi
cpu: 1000m

Rationale:

  • Extraction uses 150-300MB additional memory (temporary)
  • Class loading is CPU-bound (needs CPU during extraction)
  • Total memory = baseline (1.2GB) + extraction overhead (250-300MB)

Node Affinity

For consistent performance, run on nodes with:

  • Sufficient free RAM (at least 4GB when extractJarEnabled=true)
  • Fast disk (for initial container image pull)

Metrics & Monitoring

The startup script logs extraction metrics:

[STARTUP] JAR extraction enabled. WAR size: 250MB, Available RAM: 7200MB
[STARTUP] Extracting WAR to tmpfs: /tmp/gms/extraction
[STARTUP] WAR extracted in 2843ms
[STARTUP] Generating deterministic classpath from BOOT-INF/classpath.idx

Parse these logs to:

  • Track startup performance over time
  • Detect extraction failures early
  • Monitor RAM availability trends

Future Enhancements

  • Lazy extraction (only if WAR > threshold)
  • Parallel class loading for multi-core systems
  • Extraction pre-warming in init containers
  • Compression of classpath.idx for smaller WARs

References