pg_orrery v0.7.0 SP-GiST Benchmark — 30k Space-Track Catalog ============================================================= Date: 2026-02-17 Catalog: Space-Track USSPACECOM full catalog (29,784 objects) Host: Linux 6.16.5-arch1-1, PostgreSQL 17 Branch: phase/spgist-orbital-trie Catalog Composition: LEO (<128 min): 25,641 (86.1%) MEO (128-720 min): 1,801 ( 6.0%) GEO/HEO (720-1500 min): 2,253 ( 7.6%) Super-GEO (>1500 min): 89 ( 0.3%) Index Build: SP-GiST: 46.7 ms, 4,816 kB GiST: 65.8 ms, 5,856 kB Table: 4,760 kB ============================================================== TIMING RESULTS (best of 3 runs, ms) ============================================================== Query Pattern | Seqscan | SP-GiST | Delta -----------------------------|---------|---------|------- 2h window, Eagle ID, 10 deg | 5.19 | 5.22 | +0.03 6h window, Eagle ID, 10 deg | 5.34 | 6.34 | +1.00 24h window, Eagle ID, 10 deg | 5.42 | 6.68 | +1.26 2h window, Eagle ID, 30 deg | 5.54 | 5.64 | +0.10 2h window, equatorial, 10deg | 5.26 | 5.59 | +0.33 ============================================================== PRUNING RESULTS ============================================================== Query Pattern | Candidates | % Pass | % Pruned -----------------------------|------------|--------|--------- 2h window, Eagle ID, 10 deg | 7,188 | 24.1% | 75.9% 6h window, Eagle ID, 10 deg | 12,373 | 41.5% | 58.5% 24h window, Eagle ID, 10 deg | 26,971 | 90.6% | 9.4% 2h window, Eagle ID, 30 deg | 5,232 | 17.6% | 82.4% 2h window, equatorial, 10deg | 4,670 | 15.7% | 84.3% ============================================================== BUFFER I/O COMPARISON (2h Eagle 10deg) ============================================================== Method | Pages Read | Heap Fetches | Scan Type ------------|------------|--------------|------------------ Seqscan | 595 | n/a | Seq Scan SP-GiST | 2,396 | 0 | Index Only Scan SP-GiST reads 4.0x more pages than seqscan (2,396 vs 595). But uses Index Only Scan with zero heap fetches. ============================================================== CONSISTENCY CHECK ============================================================== False negatives: 0 (index never misses a seqscan result) False positives: 0 (index never returns extra results) Seqscan count: 7,188 SP-GiST count: 7,188 ============================================================== SCALING TREND (2h Eagle 10deg, best-of-3) ============================================================== Catalog Size | Seqscan | SP-GiST | Delta | SP-GiST Pages | Seq Pages -------------|---------|---------|--------|---------------|---------- 14,376 | 4.5 ms | 6.1 ms | +1.6ms | 888 | 291 20,597 | 3.8 ms | 4.7 ms | +0.9ms | (IOOS) | (est) 29,784 | 5.2 ms | 5.2 ms | +0.0ms | 2,396 | 595 The delta is converging toward zero. At 30k the SP-GiST index is essentially tied with seqscan on the 2h/10deg query. For queries with fewer survivors (30deg elevation, equatorial observer), the index is within 0.1-0.3ms. ============================================================== PLANNER BEHAVIOR ============================================================== PostgreSQL's query planner CHOOSES the SP-GiST index by default at 30k (without any enable_seqscan=off forcing). The planner's cost model prefers the Index Only Scan. EXPLAIN output (default settings): Index Only Scan using bench_spgist on bench_catalog Index Cond: (tle &? ...) Heap Fetches: 0 Buffers: shared hit=2396 Execution Time: 7.246 ms (with planning) ============================================================== NOTES ============================================================== 1. At 30k objects, the planner voluntarily chooses SP-GiST over seqscan. This is the crossover point where the index becomes the planner's preferred strategy. 2. The Index Only Scan with zero heap fetches means the index contains all information needed — no table access required. 3. The 75.9% pruning rate on the 2h window means only 7,188 of 29,784 satellites need SGP4 propagation. This avoids ~22,596 unnecessary SGP4 calls in the predict_passes() pipeline. 4. The equatorial observer (84.3% pruned) and high-elevation (82.4% pruned) queries show the strongest filtering because the altitude and RAAN filters are most aggressive there. 5. The 24h window only prunes 9.4% because the RAAN filter self-disables for full Earth rotations, leaving only the altitude and inclination filters active.