From c66ac63a79287ca39f1e46f4f7d0d7b5129f062f Mon Sep 17 00:00:00 2001 From: nsheff Date: Wed, 8 Jul 2026 16:20:28 -0400 Subject: [PATCH] Add namespace-scoped indexes on projects --- docs/changelog.md | 3 +++ pepdbagent/db_utils.py | 24 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 84f39ff..e727248 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -3,6 +3,9 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) and [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) format. +## [Unreleased] +- Added namespace-oriented performance indexes on `projects`: composite `(namespace, last_update_date)` for namespace project listings, a covering partial index `(namespace) INCLUDE (number_of_samples, name) WHERE private IS FALSE` for namespace aggregation, and a trigram GIN index on `namespace` for `ILIKE` search + ## [0.13.0] -- 2026-07-01 - Migrated from `peppy` to `peprs` across all modules (project, sample, view, models, utils) - Updated subsample key constant: `SUBSAMPLE_RAW_LIST_KEY` → `SUBSAMPLE_RAW_DICT_KEY` diff --git a/pepdbagent/db_utils.py b/pepdbagent/db_utils.py index fcd51c2..9755ae3 100644 --- a/pepdbagent/db_utils.py +++ b/pepdbagent/db_utils.py @@ -166,6 +166,30 @@ class Projects(Base): Index("ix_projects_last_update_date", "last_update_date"), Index("ix_projects_submission_date", "submission_date"), Index("ix_projects_number_of_stars", "number_of_stars"), + # Composite for namespace project listings + # (WHERE namespace = ? ORDER BY last_update_date DESC LIMIT n): a bounded + # index range scan instead of walking the global last_update_date index + # and filtering by namespace (the plan flip that scans most of the table + # for a namespace whose rows are not near the date tip), and no full sort. + Index("ix_projects_namespace_update_date", "namespace", "last_update_date"), + # Covering partial index for namespace aggregation + # (SELECT namespace, count(name), sum(number_of_samples) GROUP BY namespace): + # an index-only scan (~25 MB) instead of a ~250 MB heap seq scan. Covers + # only the public rows aggregated and INCLUDEs the summed/counted columns. + # (Needs a fresh visibility map — run VACUUM ANALYZE projects afterward.) + Index( + "ix_projects_ns_agg", + "namespace", + postgresql_include=["number_of_samples", "name"], + postgresql_where=text("private IS FALSE"), + ), + # Trigram GIN on namespace for namespace ILIKE '%str%' search. + Index( + "trgm_index_namespace", + "namespace", + postgresql_using="gin", + postgresql_ops={"namespace": "gin_trgm_ops"}, + ), )