diff --git a/apps/labrinth/.sqlx/query-1cefe4924d3c1f491739858ce844a22903d2dbe26f255219299f1833a10ce3d7.json b/apps/labrinth/.sqlx/query-1cefe4924d3c1f491739858ce844a22903d2dbe26f255219299f1833a10ce3d7.json deleted file mode 100644 index 6e0fd619f..000000000 --- a/apps/labrinth/.sqlx/query-1cefe4924d3c1f491739858ce844a22903d2dbe26f255219299f1833a10ce3d7.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT id FROM mods TABLESAMPLE SYSTEM_ROWS($1) WHERE status = ANY($2)\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [ - "Int8", - "TextArray" - ] - }, - "nullable": [ - false - ] - }, - "hash": "1cefe4924d3c1f491739858ce844a22903d2dbe26f255219299f1833a10ce3d7" -} diff --git a/apps/labrinth/.sqlx/query-1d017ac5f5b1e76ec241533fd4d061c79f4e6d2f1701e727a7474fd5029a5492.json b/apps/labrinth/.sqlx/query-1d017ac5f5b1e76ec241533fd4d061c79f4e6d2f1701e727a7474fd5029a5492.json new file mode 100644 index 000000000..8de44b292 --- /dev/null +++ b/apps/labrinth/.sqlx/query-1d017ac5f5b1e76ec241533fd4d061c79f4e6d2f1701e727a7474fd5029a5492.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id FROM mods WHERE status = ANY($1)\n ORDER BY id\n LIMIT $2\n OFFSET GREATEST(ROUND(RANDOM() * (SELECT COUNT(*) FROM mods WHERE status = ANY($1)))::int8 - $2, 0)", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "TextArray", + "Int8" + ] + }, + "nullable": [ + false + ] + }, + "hash": "1d017ac5f5b1e76ec241533fd4d061c79f4e6d2f1701e727a7474fd5029a5492" +} diff --git a/apps/labrinth/migrations/20250608183828_random-project-index.sql b/apps/labrinth/migrations/20250608183828_random-project-index.sql new file mode 100644 index 000000000..9d35a65b3 --- /dev/null +++ b/apps/labrinth/migrations/20250608183828_random-project-index.sql @@ -0,0 +1,12 @@ +-- This index substantially brings down the cost of the query plan for the +-- hot query at `labrinth::routes::v3::projects::random_projects_get`, from +-- 354.04..363.39 to 171.33..180.68 (~2x improvement). +-- +-- The numbers above were calculated in a clean PostgreSQL 17.5.0 container +-- with 10k mods created with the SQL below. +-- +-- WITH seq AS (SELECT n FROM GENERATE_SERIES(1, 10000) AS n) +-- INSERT INTO mods (id, team_id, name, summary, icon_url, license_url, slug, status) +-- SELECT n, 1, n, '', '', '', n, (ARRAY['approved', 'pending'])[n % 2 + 1] from seq; + +CREATE INDEX mods_status ON mods(status); diff --git a/apps/labrinth/src/routes/v3/projects.rs b/apps/labrinth/src/routes/v3/projects.rs index b78885343..2e853d8c9 100644 --- a/apps/labrinth/src/routes/v3/projects.rs +++ b/apps/labrinth/src/routes/v3/projects.rs @@ -94,14 +94,17 @@ pub async fn random_projects_get( })?; let project_ids = sqlx::query!( - " - SELECT id FROM mods TABLESAMPLE SYSTEM_ROWS($1) WHERE status = ANY($2) - ", - count.count as i32, + // IDs are randomly generated (see the `generate_ids` macro), so ID order is + // equivalent to a random order + "SELECT id FROM mods WHERE status = ANY($1) + ORDER BY id + LIMIT $2 + OFFSET GREATEST(ROUND(RANDOM() * (SELECT COUNT(*) FROM mods WHERE status = ANY($1)))::int8 - $2, 0)", &*crate::models::projects::ProjectStatus::iterator() .filter(|x| x.is_searchable()) .map(|x| x.to_string()) .collect::>(), + count.count as i32, ) .fetch(&**pool) .map_ok(|m| db_ids::DBProjectId(m.id))