mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-09-24 16:05:44 +08:00
Merge pull request #4050 from bestony/perf/keys-last-used-ip-query
perf(keys): bound latest IP lookup per key
This commit is contained in:
@@ -525,17 +525,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))
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 <> '';
|
||||
@@ -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 <> ''")
|
||||
}
|
||||
Reference in New Issue
Block a user