Buckets:

glennmatlin's picture
|
download
raw
10.6 kB

SOC-91 PACE ICE GPU Enrichment — Full Handoff (2026-03-15)

What the pipeline does

SOC-91 enriches ~56,514 deduplicated Dolma3 shards with WebOrganizer labels (topic + format classification) using GPU jobs on Georgia Tech's PACE ICE HPC cluster. The pipeline runs 4 classifiers per document: TopicClassifier, TopicClassifier-NoURL, FormatClassifier, FormatClassifier-NoURL. Output is labels-only Parquet sidecars uploaded to Cloudflare R2.

Architecture:

  • A launcher job (soc91_launcher, sbatch wrapper around launch_dual.sh) runs on a CPU node and submits GPU enrichment tasks in batches of 5
  • Each enrichment task (soc91_enrich) gets 1 GPU, reads a line from the manifest (~20 shard paths per line), downloads each shard from R2, runs 4 classifiers, uploads Parquet sidecar + .done marker + .stats.json back to R2
  • Idempotency: before processing a shard, checks if .done marker exists in R2. Safe to resubmit any task
  • State file: logs/soc91_enrich/launcher_state_coe-ice.txt tracks which manifest line the launcher will submit next. Launcher reads this on restart to resume
  • Manifest indexing: enrich_sidecar.py reads manifest line N via manifest.read_text().splitlines()[task_id]. The TASK_OFFSET env var maps SLURM array indices to manifest line numbers (since SLURM array indices are capped at 1000)

State at handoff (~22:35 EDT 2026-03-15)

  • R2 progress: 19,348/56,514 shards done (34.2%)
  • Running GPUs: 8 (severely underutilized, down from 144+ earlier in the week)
  • Pending jobs: 0 (396 blocked pending jobs were just cancelled)
  • Launcher: alive (job 4461161, ~4.1h into 8h walltime), at task 2,372/2,826
  • Launcher log shows: it's now submitting again since pending slots were freed. Was stuck repeating "At cap (404/400 active, 8 running), waiting..." before the cancel

Known problems

Problem 1: Hidden maintenance reservation blocking all pending jobs

  • All 396 pending jobs were stuck with reason ReqNodeNotAvail, Reserved for maintenance
  • SLURM's PrivateData = accounts,jobs,reservations,usage,users setting hides reservation details from non-admin users. We cannot see when maintenance starts/ends or which nodes are affected
  • Our jobs request --time=16:00:00 (16h walltime). If maintenance starts within 16h, SLURM won't schedule any new jobs because it can't guarantee completion before maintenance begins
  • Result: pending jobs consume submit slots (QOS limit = 500) but never run
  • Key question: would shorter walltime (4h or 8h) allow jobs to schedule? Each GPU processes ~10 shards/hr, so a 4h job can complete its ~20 shards if the GPU is fast enough

Problem 2: Submit slot exhaustion (QOSMaxSubmitJobPerUserLimit = 500)

  • QOS coe-ice allows max 500 submitted jobs (running + pending combined)
  • 396 dead-weight pending jobs + 8 running = 404 active, leaving almost no room
  • Launcher has its own MAX_ACTIVE=400 cap (line 9 of launch_dual.sh) and was stuck because active count exceeded it
  • As running jobs completed, GPU count declined with no replacements: 144 → 138 → 132 → 127 → 122 → 115 → 12 → 8
  • Fix applied: cancelled all 396 pending jobs via scancel -u gmatlin3 --name=soc91_enrich -t PENDING

Problem 3: Shard selection bias (sequential manifest)

  • The manifest (r2_shard_manifest.txt) is alphabetically sorted: 2,826 lines, ~20 shards each
  • Launcher processes sequentially (task 0, 1, 2...), so all 19,348 completed shards are from common_crawl subcategories starting with letters A-S
  • Zero coverage of: olmocr_science_pdfs (21,429 shards, 0%), phase2_nonpool (256 shards, 0%), and 7 common_crawl subcategories (software, software_development, sports_and_fitness, transportation, travel_and_tourism, social_life, fashion_and_beauty)
  • 8 common_crawl subcategories are at 100% while others are at 0%
  • Fix built but not deployed: build_prioritized_manifest.py creates a new manifest with 7 tiers by category completion rate, shuffled within tiers, skipping 100%-done categories. Dry-run verified: 39,294 remaining shards across ~1,965 lines

Problem 4: Draining/drained nodes

  • 6 GPU nodes currently unavailable (4 drained, 1 drained*, 1 draining)
  • As running jobs on draining nodes complete, those GPUs become permanently unavailable until maintenance ends
  • This causes the steady decline in running GPUs

Resolved misdiagnosis: "Ghost GPU allocations"

  • Initially appeared that 89 GPUs on H100/H200 nodes had zero jobs
  • Root cause: PrivateData = jobs hides other users' jobs from squeue
  • The allocations were real jobs from other users, not a bug

What was done on 2026-03-15

  1. Diagnosed the hidden maintenance reservation as root cause (not a misconfiguration on our end)
  2. Built manifest_coverage.py — reports per-subcategory completion rates vs R2
  3. Built build_prioritized_manifest.py — prioritized manifest with 7 tiers. Dry-run output:
    • T0: 28,879 shards (0% done, 16 categories including all olmocr + phase2)
    • T1: 2,130 shards (0-15% done)
    • T2: 5,350 shards (15-35% done)
    • T3: 2,176 shards (35-60% done)
    • T4: 180 shards (60-80% done)
    • T5: 538 shards (80-95% done)
    • T6: 41 shards (95-100% done)
    • Skipped: 8,130 shards from 100% complete categories
  4. Cancelled 396 blocked pending jobs to free submit slots

Action items for next session

  1. Test shorter walltime: Submit a single test job with --time=4:00:00 to see if it schedules. If it does, the maintenance window is >4h away and shorter walltimes bypass the scheduling block:

    ssh pace-ice "cd ~/dev/data-attribution-soc91 && sbatch --qos=coe-ice --time=4:00:00 --array=0-0 scripts/slurm/enrich_sidecar_gpu.sbatch"
    

    Check if it goes to RUNNING or PENDING with squeue -u gmatlin3 -h -t PENDING -o '%i %r'

  2. Deploy the prioritized manifest:

    ssh pace-ice "cd ~/dev/data-attribution-soc91 && source ~/.r2_credentials && python3 scripts/slurm/build_prioritized_manifest.py"
    

    This writes scripts/slurm/r2_shard_manifest_prioritized.txt. Then update the launcher to use it by either:

    • Setting MANIFEST=scripts/slurm/r2_shard_manifest_prioritized.txt in the sbatch environment
    • Or editing enrich_sidecar_gpu.sbatch line 102: change MANIFEST="${MANIFEST:-scripts/slurm/r2_shard_manifest.txt}" to point at the prioritized manifest
    • Reset the state file: echo 0 > logs/soc91_enrich/launcher_state_coe-ice.txt
  3. Resubmit launcher when the current one expires (~3.9h remaining on job 4461161):

    ssh pace-ice "cd ~/dev/data-attribution-soc91 && sbatch --qos=coe-ice --partition=ice-cpu --time=8:00:00 scripts/slurm/launcher.sbatch"
    

    If using shorter walltime for GPU jobs, also update enrich_sidecar_gpu.sbatch line 8 (--time=16:00:00) before resubmitting

  4. Monitor GPU recovery: after cancelling pending jobs the launcher should be submitting new tasks. Verify with:

    ssh pace-ice "squeue -u gmatlin3 -h --name=soc91_enrich -t RUNNING | wc -l"
    ssh pace-ice "squeue -u gmatlin3 -h -r --name=soc91_enrich -t PENDING | wc -l"
    ssh pace-ice "tail -5 ~/dev/data-attribution-soc91/logs/soc91_launcher/4461161.out"
    
  5. Determine maintenance window: try scontrol show reservation (may return nothing due to PrivateData), check https://pace.gatech.edu for announcements, or email pace-support@oit.gatech.edu

  6. R2 completion check:

    ssh pace-ice "source ~/.r2_credentials && python3 -c \"import boto3,os;s3=boto3.client('s3',endpoint_url='https://0934ab8e84ac8f4e81decaf3eb121337.r2.cloudflarestorage.com',aws_access_key_id=os.environ['R2_ACCESS_KEY_ID'],aws_secret_access_key=os.environ['R2_SECRET_ACCESS_KEY'],region_name='auto');p=s3.get_paginator('list_objects_v2');d=sum(1 for pg in p.paginate(Bucket='soc127-dedup',Prefix='soc91-labels/') for o in pg.get('Contents',[]) if o['Key'].endswith('.done'));print(f'r2_done={d}/56514 ({d/56514*100:.1f}%)');\""
    

Key files on cluster (~/dev/data-attribution-soc91/)

File Purpose
scripts/slurm/r2_shard_manifest.txt Current manifest (alphabetical, 2,826 lines, 56,514 shards)
scripts/slurm/r2_shard_manifest_prioritized.txt Output of prioritized builder (not yet generated)
scripts/slurm/build_prioritized_manifest.py Builds prioritized manifest from R2 .done state
scripts/slurm/manifest_coverage.py Reports per-subcategory completion rates
scripts/slurm/launch_dual.sh Launcher logic (MAX_ACTIVE=400, BATCH=5, state checkpoint)
scripts/slurm/launcher.sbatch Launcher sbatch wrapper (CPU node, 18h walltime)
scripts/slurm/enrich_sidecar_gpu.sbatch GPU enrichment job (1 GPU, 16h walltime, auto-detects VRAM/dtype)
scripts/enrich_sidecar.py Enrichment worker (reads manifest by task_id, runs 4 classifiers)
logs/soc91_enrich/launcher_state_coe-ice.txt Current state: 2372 (line number in manifest)
logs/soc91_launcher/4461161.out Current launcher log

Key constants and infrastructure

Item Value
R2 bucket soc127-dedup
R2 output prefix soc91-labels/
R2 endpoint https://0934ab8e84ac8f4e81decaf3eb121337.r2.cloudflarestorage.com
R2 credentials source ~/.r2_credentials on cluster
QOS coe-ice (max submit: 500, max GPUs: 960)
SLURM partitions ice-gpu,coe-gpu,ice-bw-gpu
GPU constraint -C nvidia-gpu (any NVIDIA GPU)
Excluded nodes atl1-1-03-014-16-0 (bad GPU)
Username gmatlin3
Worktree path ~/dev/data-attribution-soc91
Launcher MAX_ACTIVE 400 (in launch_dual.sh)
Launcher BATCH 5 tasks per submission
GPU job walltime 16:00:00 (may need reduction)
Batch size Auto-detected by VRAM (128/64/32/16)
Classifier max_length 8192 tokens
Dtype bf16 (auto-downgrades to fp16 for compute capability < 8.0)

SSH access pattern

All cluster interaction is via ssh pace-ice "command". Each invocation is a fresh login shell. No state persists between calls. The user must have an active SSH control socket (ControlMaster). If SSH times out or hangs, the user needs to re-authenticate in a separate terminal with ssh pace-ice.

Throughput baseline

  • Each GPU processes ~10 shards/hr (varies by GPU type and shard size)
  • At 144 GPUs: 1,440 shards/hr (39h for remaining 37,166 shards)
  • At 8 GPUs: 80 shards/hr (464h, not viable)
  • Target: get GPU count back above 100 by fixing the scheduling issue

Xet Storage Details

Size:
10.6 kB
·
Xet hash:
f3ef86b5ccd1a9dc564e1523a9676989b81ce750090fe2f1c99c837e38d695a5

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.