From 5a60700b40222f7812e19a86a3796a7fb89a7eab Mon Sep 17 00:00:00 2001 From: Louis Haftmann <30736553+LouisHaftmann@users.noreply.github.com> Date: Sun, 13 Sep 2026 12:53:58 +0200 Subject: [PATCH 1/2] fix(db): index cache_entries.locationId to keep storage cleanup fast Closes #260 Co-Authored-By: Claude Opus 5 (1M context) --- lib/migrations.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/migrations.ts b/lib/migrations.ts index 24ace00..f48999b 100644 --- a/lib/migrations.ts +++ b/lib/migrations.ts @@ -201,5 +201,18 @@ export function migrations( await db.schema.alterTable('storage_locations').dropColumn('sizeBytes').execute() }, }, + $6_cache_entries_locationId_index: { + async up(db) { + await db.schema + .createIndex('idx_cache_entries_locationId') + .on('cache_entries') + .columns(['locationId']) + .execute() + }, + async down(db) { + const dropIndex = db.schema.dropIndex('idx_cache_entries_locationId') + await (driver === 'mysql' ? dropIndex.on('cache_entries') : dropIndex).execute() + }, + }, } satisfies Record } From d8c5f5af18e7c2420d0d1d5d0f6612f31370824e Mon Sep 17 00:00:00 2001 From: Louis Haftmann <30736553+LouisHaftmann@users.noreply.github.com> Date: Sun, 13 Sep 2026 13:03:00 +0200 Subject: [PATCH 2/2] fix(db): drop indexes with MySQL-compatible syntax in down migrations Co-Authored-By: Claude Opus 5 (1M context) --- lib/migrations.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/migrations.ts b/lib/migrations.ts index f48999b..8f28ec2 100644 --- a/lib/migrations.ts +++ b/lib/migrations.ts @@ -1,4 +1,5 @@ import type { Hookable } from 'hookable' +import type { Kysely } from 'kysely' import type { Migration } from 'kysely/migration' import type { Env } from './schemas' import { Storage } from './storage' @@ -9,6 +10,12 @@ export function migrations( afterMigrate: () => Promise }>, ) { + // MySQL requires `DROP INDEX ... ON table`, other drivers reject it + async function dropIndex(db: Kysely, index: string, table: string) { + const query = db.schema.dropIndex(index) + await (driver === 'mysql' ? query.on(table) : query).execute() + } + return { $0_init: { async up(db) { @@ -110,9 +117,9 @@ export function migrations( await db.schema.createIndex('idx_uploads_scope').on('uploads').columns(['scope']).execute() }, async down(db) { - await db.schema.dropIndex('idx_cache_entries_scope').execute() + await dropIndex(db, 'idx_cache_entries_scope', 'cache_entries') await db.schema.alterTable('cache_entries').dropColumn('scope').execute() - await db.schema.dropIndex('idx_uploads_scope').execute() + await dropIndex(db, 'idx_uploads_scope', 'uploads') await db.schema.alterTable('uploads').dropColumn('scope').execute() }, }, @@ -152,9 +159,9 @@ export function migrations( .execute() }, async down(db) { - await db.schema.dropIndex('idx_cache_entries_repoId').execute() + await dropIndex(db, 'idx_cache_entries_repoId', 'cache_entries') await db.schema.alterTable('cache_entries').dropColumn('repoId').execute() - await db.schema.dropIndex('idx_uploads_repoId').execute() + await dropIndex(db, 'idx_uploads_repoId', 'uploads') await db.schema.alterTable('uploads').dropColumn('repoId').execute() }, }, @@ -210,8 +217,7 @@ export function migrations( .execute() }, async down(db) { - const dropIndex = db.schema.dropIndex('idx_cache_entries_locationId') - await (driver === 'mysql' ? dropIndex.on('cache_entries') : dropIndex).execute() + await dropIndex(db, 'idx_cache_entries_locationId', 'cache_entries') }, }, } satisfies Record