From 80b7a8d4cbc8e17676fa9ac9d751bc39eacd75fe Mon Sep 17 00:00:00 2001 From: Bestony Date: Sat, 11 Jul 2026 17:31:30 +0800 Subject: [PATCH 1/2] perf(keys): bound latest IP lookup per key --- backend/internal/repository/api_key_repo.go | 22 ++++++++++--------- .../api_key_repo_last_used_unit_test.go | 15 +++++++++++++ 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/backend/internal/repository/api_key_repo.go b/backend/internal/repository/api_key_repo.go index d348ef29b7..c98b7d087a 100644 --- a/backend/internal/repository/api_key_repo.go +++ b/backend/internal/repository/api_key_repo.go @@ -524,17 +524,19 @@ func (r *apiKeyRepository) latestUsageLogIPs(ctx context.Context, apiKeyIDs []in func latestUsageLogIPsQuery(apiKeyIDs []int64, dialectName string) (string, []any) { if dialectName == dialect.Postgres { + // Keep each key lookup bounded to one ordered index probe instead of ranking its full history. return ` - SELECT api_key_id, ip_address - FROM ( - SELECT api_key_id, ip_address, - ROW_NUMBER() OVER (PARTITION BY api_key_id ORDER BY created_at DESC, id DESC) AS rn - FROM usage_logs - WHERE api_key_id = ANY($1::bigint[]) - AND ip_address IS NOT NULL - AND ip_address <> '' - ) ranked - WHERE rn = 1`, []any{pq.Array(apiKeyIDs)} + SELECT requested.api_key_id, latest.ip_address + FROM unnest($1::bigint[]) AS requested(api_key_id) + CROSS JOIN LATERAL ( + SELECT ul.ip_address + FROM usage_logs AS ul + WHERE ul.api_key_id = requested.api_key_id + AND ul.ip_address IS NOT NULL + AND ul.ip_address <> '' + ORDER BY ul.created_at DESC, ul.id DESC + LIMIT 1 + ) AS latest`, []any{pq.Array(apiKeyIDs)} } placeholders := make([]string, len(apiKeyIDs)) diff --git a/backend/internal/repository/api_key_repo_last_used_unit_test.go b/backend/internal/repository/api_key_repo_last_used_unit_test.go index 839eda7f75..dbdf653f8a 100644 --- a/backend/internal/repository/api_key_repo_last_used_unit_test.go +++ b/backend/internal/repository/api_key_repo_last_used_unit_test.go @@ -3,6 +3,7 @@ package repository import ( "context" "database/sql" + "strings" "testing" "time" @@ -125,6 +126,20 @@ func TestAPIKeyRepositoryListByUserIDAttachesLastUsedIP(t *testing.T) { require.Nil(t, byID[noLogs.ID].LastUsedIP) } +func TestLatestUsageLogIPsQueryPostgresUsesPerKeyLateralLookup(t *testing.T) { + query, args := latestUsageLogIPsQuery([]int64{11, 22}, dialect.Postgres) + normalizedQuery := strings.Join(strings.Fields(query), " ") + + require.Contains(t, normalizedQuery, "FROM unnest($1::bigint[]) AS requested(api_key_id)") + require.Contains(t, normalizedQuery, "CROSS JOIN LATERAL") + require.Contains(t, normalizedQuery, "WHERE ul.api_key_id = requested.api_key_id") + require.Contains(t, normalizedQuery, "AND ul.ip_address IS NOT NULL") + require.Contains(t, normalizedQuery, "AND ul.ip_address <> ''") + require.Contains(t, normalizedQuery, "ORDER BY ul.created_at DESC, ul.id DESC LIMIT 1") + require.NotContains(t, normalizedQuery, "ROW_NUMBER") + require.Len(t, args, 1) +} + func TestAPIKeyRepository_CreateWithLastUsedAt(t *testing.T) { repo, client := newAPIKeyRepoSQLite(t) ctx := context.Background() From 1c02158c2a7fcdf97540ccc719df5323667146af Mon Sep 17 00:00:00 2001 From: Bestony Date: Sat, 11 Jul 2026 17:32:06 +0800 Subject: [PATCH 2/2] perf(migrations): index latest API key IP lookups --- .../internal/repository/migrations_runner.go | 4 ++ .../repository/migrations_runner_notx_test.go | 39 +++++++++++++++++++ ...sage_logs_api_key_latest_ip_index_notx.sql | 5 +++ .../latest_api_key_ip_index_test.go | 19 +++++++++ 4 files changed, 67 insertions(+) create mode 100644 backend/migrations/174_add_usage_logs_api_key_latest_ip_index_notx.sql create mode 100644 backend/migrations/latest_api_key_ip_index_test.go diff --git a/backend/internal/repository/migrations_runner.go b/backend/internal/repository/migrations_runner.go index 7c045fea74..a071967f65 100644 --- a/backend/internal/repository/migrations_runner.go +++ b/backend/internal/repository/migrations_runner.go @@ -55,6 +55,8 @@ const paymentOrdersOutTradeNoUniqueMigration = "120_enforce_payment_orders_out_t const paymentOrdersOutTradeNoUniqueIndex = "paymentorder_out_trade_no_unique" const schedulerOutboxPendingDedupKeyMigration = "153_scheduler_outbox_pending_dedup_key_index_notx.sql" const schedulerOutboxPendingDedupKeyIndex = "idx_scheduler_outbox_pending_dedup_key" +const latestAPIKeyIPIndexMigration = "174_add_usage_logs_api_key_latest_ip_index_notx.sql" +const latestAPIKeyIPIndex = "idx_usage_logs_api_key_latest_ip" type migrationChecksumCompatibilityRule struct { fileChecksum string @@ -264,6 +266,8 @@ func prepareNonTransactionalMigration(ctx context.Context, db *sql.DB, name stri return preparePaymentOrdersOutTradeNoUniqueMigration(ctx, db) case schedulerOutboxPendingDedupKeyMigration: return dropInvalidIndexIfPresent(ctx, db, schedulerOutboxPendingDedupKeyIndex) + case latestAPIKeyIPIndexMigration: + return dropInvalidIndexIfPresent(ctx, db, latestAPIKeyIPIndex) default: return nil } diff --git a/backend/internal/repository/migrations_runner_notx_test.go b/backend/internal/repository/migrations_runner_notx_test.go index c9f6a2cdf1..6bb7914b95 100644 --- a/backend/internal/repository/migrations_runner_notx_test.go +++ b/backend/internal/repository/migrations_runner_notx_test.go @@ -116,6 +116,45 @@ CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_t_b ON t(b); require.NoError(t, mock.ExpectationsWereMet()) } +func TestApplyMigrationsFS_NonTransactionalMigration_LatestAPIKeyIPIndexDropsInvalidIndexBeforeRetry(t *testing.T) { + db, mock, err := sqlmock.New() + require.NoError(t, err) + defer func() { _ = db.Close() }() + + prepareMigrationsBootstrapExpectations(mock) + mock.ExpectQuery("SELECT checksum FROM schema_migrations WHERE filename = \\$1"). + WithArgs(latestAPIKeyIPIndexMigration). + WillReturnError(sql.ErrNoRows) + mock.ExpectQuery("SELECT EXISTS \\("). + WithArgs(latestAPIKeyIPIndex). + WillReturnRows(sqlmock.NewRows([]string{"exists"}).AddRow(true)) + mock.ExpectExec("DROP INDEX CONCURRENTLY IF EXISTS idx_usage_logs_api_key_latest_ip"). + WillReturnResult(sqlmock.NewResult(0, 0)) + mock.ExpectExec("CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_usage_logs_api_key_latest_ip"). + WillReturnResult(sqlmock.NewResult(0, 0)) + mock.ExpectExec("INSERT INTO schema_migrations \\(filename, checksum\\) VALUES \\(\\$1, \\$2\\)"). + WithArgs(latestAPIKeyIPIndexMigration, sqlmock.AnyArg()). + WillReturnResult(sqlmock.NewResult(1, 1)) + mock.ExpectExec("SELECT pg_advisory_unlock\\(\\$1\\)"). + WithArgs(migrationsAdvisoryLockID). + WillReturnResult(sqlmock.NewResult(0, 1)) + + fsys := fstest.MapFS{ + latestAPIKeyIPIndexMigration: &fstest.MapFile{ + Data: []byte(` +CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_usage_logs_api_key_latest_ip + ON usage_logs (api_key_id, created_at DESC, id DESC) + INCLUDE (ip_address) + WHERE ip_address IS NOT NULL AND ip_address <> ''; +`), + }, + } + + err = applyMigrationsFS(context.Background(), db, fsys) + require.NoError(t, err) + require.NoError(t, mock.ExpectationsWereMet()) +} + func TestApplyMigrationsFS_PaymentOrdersOutTradeNoUniqueMigration_FailsFastOnDuplicatePrecheck(t *testing.T) { db, mock, err := sqlmock.New() require.NoError(t, err) diff --git a/backend/migrations/174_add_usage_logs_api_key_latest_ip_index_notx.sql b/backend/migrations/174_add_usage_logs_api_key_latest_ip_index_notx.sql new file mode 100644 index 0000000000..261698f8cc --- /dev/null +++ b/backend/migrations/174_add_usage_logs_api_key_latest_ip_index_notx.sql @@ -0,0 +1,5 @@ +-- Support the per-key latest non-empty source IP lookup without scanning full key history. +CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_usage_logs_api_key_latest_ip + ON usage_logs (api_key_id, created_at DESC, id DESC) + INCLUDE (ip_address) + WHERE ip_address IS NOT NULL AND ip_address <> ''; diff --git a/backend/migrations/latest_api_key_ip_index_test.go b/backend/migrations/latest_api_key_ip_index_test.go new file mode 100644 index 0000000000..1de64a9ff5 --- /dev/null +++ b/backend/migrations/latest_api_key_ip_index_test.go @@ -0,0 +1,19 @@ +package migrations + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestLatestAPIKeyIPIndexMigration(t *testing.T) { + content, err := FS.ReadFile("174_add_usage_logs_api_key_latest_ip_index_notx.sql") + require.NoError(t, err) + + sql := strings.Join(strings.Fields(string(content)), " ") + require.Contains(t, sql, "CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_usage_logs_api_key_latest_ip") + require.Contains(t, sql, "ON usage_logs (api_key_id, created_at DESC, id DESC)") + require.Contains(t, sql, "INCLUDE (ip_address)") + require.Contains(t, sql, "WHERE ip_address IS NOT NULL AND ip_address <> ''") +}