Skip to content

Fix sso duplication issue and allow for bulk reading of sso file - #39

Open
axf295 wants to merge 3 commits into
mainfrom
fix-get-box-sso-ra-wraparound-dedup
Open

Fix sso duplication issue and allow for bulk reading of sso file#39
axf295 wants to merge 3 commits into
mainfrom
fix-get-box-sso-ra-wraparound-dedup

Conversation

@axf295

@axf295 axf295 commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Asked claude how to make it more efficient to read in the ephemerides, and this is what it came up with. also allow indexing by time and by sso_id so that it's much quicker to query.

also include a single script to bulk load act catalog and ssos, since we'll want to do that every time.

Allen M. Foster and others added 3 commits July 17, 2026 09:32
When a sky box crosses RA=0/360 (lower_left.ra > upper_right.ra),
get_box_sso split the query into two RA sub-ranges combined with
union_all(). Neither sub-query deduplicated its own outerjoin against
moving_sources, so an object with N matching ephemeris points came
back as N duplicate rows. The outer .distinct() didn't help either,
since it was applied before .from_statement(), which replaces the
executed SQL wholesale and silently drops it.

Fixed by deduplicating each RA sub-range individually and switching
the outer combine from union_all to union, so an object with
ephemeris points on both sides of RA=0 within the same window isn't
double-counted either.

Found via a real production DB (~50M ephemeris rows, 641 SSOs) where
a wraparound box for a field straddling RA=0 returned one object
duplicated ~83 times, and the resulting per-object get_source_generator
fan-out (once per duplicate) took get_box() from ~1s to ~118s.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
RegisteredMovingSourceTable (the ORM model, used by
SQLModel.metadata.create_all() -- which is what every Client(db_url=...)
call uses when provisioning a fresh database, including the initial
Alembic migration's own table) had no index beyond its primary key. The
Alembic migration path separately indexed `time` but never `sso_id`, so
the two schema-provisioning paths in this repo disagreed with each other,
and the one actually used in practice (create_all()) was the worse of
the two.

Adds a (sso_id, time) composite index to the ORM model (serves both
get_ephem_points()/get_source_generator()'s sso_id-first lookups and,
via leftmost-prefix, plain sso_id lookups) and makes `time` itself
Field(index=True) for get_box_sso()/get_monitored_ssos()/
get_pointing_ssos(), which have no sso_id to filter on. A companion
Alembic migration adds the same composite index for databases already
provisioned via `alembic upgrade head`, which already had the time
index from the initial migration.

Verified both provisioning paths (SQLModel.metadata.create_all() and
`alembic upgrade head`, including an upgrade/downgrade/upgrade
round-trip) now produce the identical index set.

Without these, every per-object ephemeris lookup and every get_box_sso
call is a full table scan -- fine at test scale, not at the tens of
millions of rows a real ephemeris table holds (see simonsobs/sotrplib's
scripts/solar_system/build_socat_db.py).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…files

socat.ingest.actfits.ingest_fits_file/jplparquet.ingest_jpl_parquet_file
go through a ClientBase (one INSERT + COMMIT per row via create_source()/
create_sso()/create_ephem()), which is right for adding a handful of
sources to an existing catalog but not for building a database from
scratch: a real ephemeris ingest at these files' ~2-hour cadence over
2015-2033 is tens of millions of rows, and one-commit-per-row would take
on the order of days.

New socat.ingest.bulk_sqlite (CLI: socat-build-db) bulk-loads an ACT FITS
catalog and JPL Horizons ephemeris parquet files directly via raw
sqlite3.executemany with SQLite tuned for bulk writes, building on local
disk before copying to the requested destination (SQLite doesn't behave
reliably over network filesystems like Lustre/NFS, which is usually where
the final database needs to live). No manual index-creation step is
needed -- create_schema() uses socat's own SQLModel metadata, so the
indexes added to RegisteredMovingSourceTable in the previous commit are
applied automatically.

Verified against real production inputs (a 90k-row ACT catalog, a
50M+-row two-file JPL ephemeris set) as well as new synthetic-fixture
tests covering schema/index creation, fixed-source loading, designation
parsing, ephemeris loading, and a full build() round-trip through a real
Client query.

Also declares numpy and tqdm as direct dependencies -- both are imported
directly (tqdm already was, by the existing jplparquet ingest module,
but was undeclared and not actually installable from a clean checkout).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant