diff --git a/.changeset/shaky-berries-stay.md b/.changeset/shaky-berries-stay.md new file mode 100644 index 000000000..e327c0d07 --- /dev/null +++ b/.changeset/shaky-berries-stay.md @@ -0,0 +1,5 @@ +--- +"emdash": patch +--- + +Adds composite index on (deleted_at, published_at DESC, id DESC) to eliminate full table scans for frontend listing queries that order by published_at. diff --git a/packages/core/src/database/migrations/034_published_at_index.ts b/packages/core/src/database/migrations/034_published_at_index.ts new file mode 100644 index 000000000..d7cbc6750 --- /dev/null +++ b/packages/core/src/database/migrations/034_published_at_index.ts @@ -0,0 +1,29 @@ +import type { Kysely } from "kysely"; +import { sql } from "kysely"; + +import { listTablesLike } from "../dialect-helpers.js"; + +export async function up(db: Kysely): Promise { + const tableNames = await listTablesLike(db, "ec_%"); + + for (const tableName of tableNames) { + const table = { name: tableName }; + + await sql` + CREATE INDEX ${sql.ref(`idx_${table.name}_deleted_published_id`)} + ON ${sql.ref(table.name)} (deleted_at, published_at DESC, id DESC) + `.execute(db); + } +} + +export async function down(db: Kysely): Promise { + const tableNames = await listTablesLike(db, "ec_%"); + + for (const tableName of tableNames) { + const table = { name: tableName }; + + await sql`DROP INDEX IF EXISTS ${sql.ref(`idx_${table.name}_deleted_published_id`)}`.execute( + db, + ); + } +} diff --git a/packages/core/src/database/migrations/runner.ts b/packages/core/src/database/migrations/runner.ts index 838c6b38a..51d43621d 100644 --- a/packages/core/src/database/migrations/runner.ts +++ b/packages/core/src/database/migrations/runner.ts @@ -34,6 +34,7 @@ import * as m030 from "./030_widen_scheduled_index.js"; import * as m031 from "./031_bylines.js"; import * as m032 from "./032_rate_limits.js"; import * as m033 from "./033_optimize_content_indexes.js"; +import * as m034 from "./034_published_at_index.js"; const MIGRATIONS: Readonly> = Object.freeze({ "001_initial": m001, @@ -68,6 +69,7 @@ const MIGRATIONS: Readonly> = Object.freeze({ "031_bylines": m031, "032_rate_limits": m032, "033_optimize_content_indexes": m033, + "034_published_at_index": m034, }); /** Total number of registered migrations. Exported for use in tests. */ diff --git a/packages/core/src/schema/registry.ts b/packages/core/src/schema/registry.ts index 97d8a2763..4b6666bac 100644 --- a/packages/core/src/schema/registry.ts +++ b/packages/core/src/schema/registry.ts @@ -595,6 +595,11 @@ export class SchemaRegistry { CREATE INDEX ${sql.ref(`idx_${tableName}_deleted_created_id`)} ON ${sql.ref(tableName)} (deleted_at, created_at DESC, id DESC) `.execute(conn); + + await sql` + CREATE INDEX ${sql.ref(`idx_${tableName}_deleted_published_id`)} + ON ${sql.ref(tableName)} (deleted_at, published_at DESC, id DESC) + `.execute(conn); } /**